import sys
# THE WEB-EDITOR FIX: Re-inject the system argument if the web editor forgets it on reload.
if not sys.argv:
    sys.argv.append("game.py")

import pyxel
import math
import random
import webbrowser

# ==========================================
# --- GLOBALS & CONSTANTS (TRUE 512 HD SCALE) ---
# ==========================================
SCREEN_WIDTH = 512
SCREEN_HEIGHT = 512
GRAVITY = 1.2               
FLOOR_Y = SCREEN_HEIGHT - 64 
PLAYER_SPEED = 6            
JUMP_STRENGTH = -18         

# ==========================================
# --- NEW CLASSES (PARTICLES & TEXT) ---
# ==========================================
class Particle:
    def __init__(self, x, y, vx, vy, life, color, gravity):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.life = life
        self.color = color
        self.gravity = gravity

    def update(self):
        self.x += self.vx
        self.y += self.vy
        if self.gravity:
            self.vy += GRAVITY * 0.5
        self.life -= 1

    def draw(self):
        pyxel.pset(self.x, self.y, self.color)

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

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

# ==========================================
# --- MAIN GAME CLASS ---
# ==========================================
class Game:
    """
    MAIN CAMPAIGN CLASS - 512x512 HD EDITION
    """
    def __init__(self):
        pyxel.init(SCREEN_WIDTH, SCREEN_HEIGHT, title="The Malevolent Witch HD")
        pyxel.mouse(True) 
        
        self.state = 0
        self.level = 1
        self.coins = 0
        
        self.story_timer = 0
        self.has_seen_story = False
        self.game_beaten = False # Unlocks the Endless Mode link
        
        self.music_vol = 3 
        self.sfx_vol = 6
        
        self.damage_lv = 1
        self.proj_speed_lv = 1
        self.hp_lv = 1
        self.max_hp = 100
        
        self.unlocked_spells = []
        self.active_spell = None
        
        self.dev_unlocked = False
        self.immortal = False
        self.dev_dmg_slider = 0.1 
        self.slider_drag = False
        
        self.particles = []
        self.explosions = [] 
        self.flames = [] 
        self.floating_texts = []      
        self.shake = 0    
        self.flash = 0    
        
        self.p_hp_disp = 100
        self.boss_hp_disp = 100
        
        self.FONT = {
            'A':[2,5,7,5,5], 'B':[6,5,6,5,6], 'C':[3,4,4,4,3], 'D':[6,5,5,5,6], 'E':[7,4,6,4,7],
            'F':[7,4,6,4,4], 'G':[3,4,5,5,3], 'H':[5,5,7,5,5], 'I':[7,2,2,2,7], 'J':[1,1,1,5,2],
            'K':[5,5,6,5,5], 'L':[4,4,4,4,7], 'M':[5,7,5,5,5], 'N':[5,7,7,5,5], 'O':[2,5,5,5,2],
            'P':[7,5,7,4,4], 'Q':[2,5,5,5,3], 'R':[6,5,6,5,5], 'S':[3,4,2,1,6], 'T':[7,2,2,2,2],
            'U':[5,5,5,5,2], 'V':[5,5,5,5,2], 'W':[5,5,5,7,5], 'X':[5,5,2,5,5], 'Y':[5,5,2,2,2],
            'Z':[7,1,2,4,7], '0':[2,5,5,5,2], '1':[2,6,2,2,7], '2':[6,1,2,4,7], '3':[6,1,2,1,6],
            '4':[5,5,7,1,1], '5':[7,4,6,1,6], '6':[3,4,7,5,2], '7':[7,1,2,2,2], '8':[2,5,2,5,2],
            '9':[2,5,7,1,6], ' ':[0,0,0,0,0], '-':[0,0,7,0,0], '.':[0,0,0,0,2], '/':[1,2,2,4,4],
            ':':[0,2,0,2,0], '!':[2,2,2,0,2], '(':[2,4,4,4,2], ')':[4,2,2,2,4], '<':[1,2,4,2,1], 
            '>':[4,2,1,2,4], '[':[6,4,4,4,6], ']':[3,1,1,1,3], '?':[2,5,1,2,0], ',':[0,0,0,2,1]
        }
        
        self.init_environment()
        
        self.credits_list = [
            "THE MALEVOLENT WITCH", "", "Game Idea: Jashan and Tony",
            "Main Coder: Gemini", "Co-Coder: Chagbt", "Most Useful Coder: Gemini",
            "Visionary Overlord: Jashan", "Executive Mastermind: Jashan",
            "Artist: Gemini", "Composer: Gemini", "Credits Maker: Gemini", 
            "Debugger: Gemini and Jashan", "Emotional Support: Tony",
            "Manager: Jashan", "Investors: Jashan and Tony", "Game Tester: Jashan",
            "Game Approver: Tony", "Chief Sleeper: Tony", "Chief Procrastinator: Tony",
            "Janitor: Tony", "Maid : Tony", "Chief executive officer : Jashan",
            "", "THANK YOU FOR PLAYING!"
        ]
        self.credits_y = 512 
        
        self.init_audio()
        self.reset_game()
        
        pyxel.run(self.update, self.draw)

    def init_environment(self):
        self.rain = [[random.randint(0, 512), random.randint(0, 512)] for _ in range(120)]
        self.water_splashes = []
        self.mist = [[random.randint(0, 512), random.randint(310, 340), random.uniform(0.4, 1.0)] for _ in range(24)]
        self.leaves = [[random.randint(0, 600), random.randint(0, 400), random.randint(0, 2)] for _ in range(50)]
        self.bird = {"x": -40, "y": 100, "target_y": 100, "swooping": False}

        self.flowers = []
        for _ in range(180):
            self.flowers.append({"x": random.randint(0, 512), "y": random.randint(360, 500), "color": random.choice([7, 8, 9, 10, 11, 12, 14]), "size": random.randint(2, 4), "sway_offset": random.uniform(0, math.pi * 2)})
            
        self.fireflies = [[random.randint(20, 492), random.randint(200, 340), random.randint(0, 30), random.uniform(-0.6, 0.6)] for _ in range(16)]
        
        self.butterflies = []
        for _ in range(15):
            self.butterflies.append({"x": random.randint(60, 452), "y": random.randint(320, 440), "vx": random.uniform(-2, 2), "vy": random.uniform(-1.0, 1.0), "color": random.choice([8, 9, 10, 12]), "flap_timer": 0})
            
        self.bird_flock = []
        for _ in range(8):
            self.bird_flock.append({"x": random.randint(-200, -40), "y": random.randint(40, 160), "speed": random.uniform(0.6, 1.2), "w_offset": random.uniform(0, 10)})
            
        self.whale = {"active": False, "x": -100, "y": 200, "vx": 2, "vy": -1} 

        self.autumn_leaves = []
        for _ in range(50):
            c_pairs = [(9, 8), (10, 9), (8, 4), (4, 9), (11, 3)] 
            pair = random.choices(c_pairs, weights=[4, 3, 3, 2, 1])[0]
            self.autumn_leaves.append([random.randint(0, 512), random.randint(-40, 460), random.uniform(1.0, 2.4), random.randint(0, 2), pair[0], pair[1]])
            
        self.wind_timer = 0
        self.mage_blood_drips = []
        self.blood_timer = 0
        self.dove = {"x": -80, "y": 60, "state": "flying"}

    def init_audio(self):
        sv = str(self.sfx_vol)
        mv = str(max(0, self.music_vol - 2)) 
        m0 = ("a1 c2 e2 g1 b1 d2 f1 a1 c2 c1 e1 g1 d1 f1 a1 g1 b1 d2 e1 g#1 b1")
        pyxel.sound(0).set(m0, "p", mv, "v", 240) 
        m1 = ("c2 r e2 r b1 r d2 r a1 r c2 r e1 r g1 r f1 r a1 r d1 r f1 r g#1 r b1 r")
        pyxel.sound(1).set(m1, "s", mv, "v", 240) 
        m2 = ("a2 r c3 r g2 r b2 r f2 r a2 r c2 r e2 r d2 r f2 r g2 r b2 r e2 r g#2 r")
        pyxel.sound(2).set(m2, "t", mv, "n", 240) 

        pyxel.sound(50).set("a3a2", "p", sv, "f", 4)      
        pyxel.sound(51).set("a0a0", "n", sv, "f", 8)      
        pyxel.sound(52).set("c2e2", "s", sv, "f", 4)      
        pyxel.sound(53).set("c4e4", "t", sv, "n", 3)      
        pyxel.sound(54).set("c1", "n", sv, "f", 6)        
        pyxel.sound(55).set("c2", "n", sv, "f", 2)        
        pyxel.sound(56).set("c2e2g2c3", "s", sv, "f", 12) 
        pyxel.sound(57).set("a1a0", "n", sv, "f", 10)     
        pyxel.sound(58).set("e4e4e4e4e4e4", "n", sv, "f", 25) 
        pyxel.sound(60).set("c2", "t", sv, "f", 3)        
        pyxel.sound(61).set("a0a0a0", "s", sv, "f", 20)   
        pyxel.sound(62).set("a0a0", "n", sv, "f", 15)     
        pyxel.sound(63).set("a0c0e0", "n", sv, "f", 15)   

    def play_music(self):
        pyxel.stop() 
        if self.music_vol == 0: return 
        mv = str(max(0, self.music_vol - 2)) 
        
        if self.state in [7, 8]:
            pyxel.play(0, [0], loop=True); pyxel.play(1, [1], loop=True); pyxel.play(2, [2], loop=True)
        elif getattr(self, 'witch_darkness', False) or self.level == 0:
            return 
        elif self.level in [1, 2]:
            s0 = ("d2f2a2d3 d2f2a2d3 a1c2e2a2 a1c2e2a2 f1a1c2f2 f1a1c2f2 g1a#1d2g2 g1a#1d2g2 d2f2a2f3 d2f2a2f3 a1c2e2c3 a1c2e2c3 f1a1d2f2 f1a1d2f2 g1a#1d2g2 a1c#2e2a2")
            pyxel.sound(0).set(s0, "p", mv, "v", 50) 
            s1 = ("d1rrrrrr d1rrrrrr a0rrrrrr a0rrrrrr f0rrrrrr f0rrrrrr g0rrrrrr g0rrrrrr d1rrd1rr d1rrd1rr a0rra0rr a0rra0rr f0rrf0rr f0rrf0rr g0rrg0rr a0rra0rr")
            pyxel.sound(1).set(s1, "s", mv, "n", 50)
            s2 = ("c1rrrrrr c1rrrrrr c1rrrrrr c1rrrrrr c1rrc1rr c1rrc1rr c1rrc1rr c1rrc1rr")
            pyxel.sound(2).set(s2, "n", mv, "f", 50)
        elif self.level in [3, 4]:
            s0 = ("e2g2b2e3 e2g2b2e3 c2e2g2c3 c2e2g2c3 a1c2e2a2 a1c2e2a2 b1d#2f#2b2 b1d#2f#2b2 e2g2b2g3 e2g2b2g3 c2e2g2e3 c2e2g2e3 a1c2e2c3 a1c2e2c3 b1d#2f#2d#3 b1d#2f#2d#3")
            pyxel.sound(0).set(s0, "p", mv, "v", 50)
            s1 = ("e1rrrrrr e1rrrrrr c1rrrrrr c1rrrrrr a0rrrrrr a0rrrrrr b0rrrrrr b0rrrrrr e1rre1rr e1rre1rr c1rrc1rr c1rrc1rr a0rra0rr a0rra0rr b0rrb0rr b0rrb0rr")
            pyxel.sound(1).set(s1, "s", mv, "n", 50)
            s2 = ("c1rrrrrr c1rrrrrr c1rrrrrr c1rrrrrr c1rrc1rr c1rrc1rr c1rrc1rr c1rrc1rr")
            pyxel.sound(2).set(s2, "n", mv, "f", 50)
        elif self.level in [5, 6]:
            s0 = ("f2a2c3f3 f2a2c3f3 d2f2a2d3 d2f2a2d3 a#1d2f2a#2 a#1d2f2a#2 c2e2g2c3 c2e2g2c3 f2a2c3a3 f2a2c3a3 e2g2c3e3 e2g2c3e3 d2f2a2d3 d2f2a2d3 c#2e2a2c#3 c#2e2a2c#3")
            pyxel.sound(0).set(s0, "p", mv, "v", 50)
            s1 = ("f1rrrrrr f1rrrrrr d1rrrrrr d1rrrrrr a#0rrrrrr a#0rrrrrr c1rrrrrr c1rrrrrr f1rrf1rr f1rrf1rr e1rre1rr e1rre1rr d1rrd1rr d1rrd1rr c#1rrc#1rr c#1rrc#1rr")
            pyxel.sound(1).set(s1, "t", mv, "n", 50)
            s2 = ("c1rrrrrr c1rrrrrr c1rrrrrr c1rrrrrr c1rrc1rr c1rrc1rr c1rrc1rr c1rrc1rr")
            pyxel.sound(2).set(s2, "n", mv, "f", 50)
        else: 
            s0 = ("d2 f2 a2 d3 a1 c#2 e2 a2 a#1 d2 f2 a#2 g1 a#1 d2 g2")
            pyxel.sound(0).set(s0, "s", mv, "v", 60)
            s1 = ("d1 r r r a0 r r r a#0 r r r g0 r r r")
            pyxel.sound(1).set(s1, "p", mv, "n", 60)
            pyxel.sound(2).set("c1 r r r r r r r", "n", mv, "f", 60)

        pyxel.play(0, 0, loop=True); pyxel.play(1, 1, loop=True); pyxel.play(2, 2, loop=True)

    def play_sfx(self, sound_id):
        if self.sfx_vol > 0: pyxel.play(3, sound_id)

    def reset_game(self):
        self.witch_darkness = False
        self.play_music()
        
        self.px, self.py = 80, FLOOR_Y - 64
        self.p_vx, self.p_vy = 0, 0
        self.p_hp = self.max_hp
        self.p_hp_disp = self.max_hp
        self.p_attack_cd = 0 
        self.p_slow_timer = 0
        self.spell_cooldown = 0
        self.max_cd = 60
        self.projectiles = []
        
        hp_scaling = [100, 300, 500, 800, 1200, 2000, 3500]
        if self.level == 0: 
            self.boss_hp = 9999; self.boss_max_hp = 9999
            self.tut_step = 0; self.tut_timer = 0
        else:
            self.boss_hp = hp_scaling[max(0, min(6, self.level - 1))]
            self.boss_max_hp = self.boss_hp
            
        self.boss_hp_disp = self.boss_max_hp
        self.boss_immune = False
        self.boss_timer = 0
        self.boss_vy = 0
        self.boss_action = 0 
        
        self.boss_on_fire = 0
        self.boss_freeze_stacks = 0
        self.boss_frozen_timer = 0
        self.p_poisoned = False
        self.flame_dmg_cd = 0 
        
        self.bombs = []; self.boss_bullets = []; self.squires = []
        self.squires_spawned = False
        self.geyser_warns = []; self.geysers = []; self.lava_timer = 0
        self.web_traps = []; self.tiny_spiders = []; self.latched_spiders = 0
        self.swarm_timer = 0; self.exploded = False
        self.soul_heads = []; self.skeletons = []; self.earth_spikes = []
        self.skeletons_killed = 0; self.skeletons_to_spawn = 20; self.witch_quakes = 0 
        
        self.particles.clear(); self.explosions.clear()
        self.flames.clear(); self.floating_texts.clear()
        self.boss_aim_angle = 0 
        
        self.b_target_x = 400 
        self.bx, self.by = 400, FLOOR_Y
        self.b_dir = -1
        self.platforms = []
        
        if self.level == 0: self.by = FLOOR_Y
        elif self.level == 1: self.by = 160
        elif self.level == 2:
            self.swing_angle = 0 
            self.platforms = [[60, 320, 100, 150, 0, 150, 150, False], [352, 320, 100, 150, 0, 150, 150, False]]
        elif self.level == 3:
            self.platforms = [[80, 360, 80, 150, 0, 150, 150, False], [352, 360, 80, 150, 0, 150, 150, False], [216, 240, 80, 150, 0, 150, 150, False]]
        elif self.level == 6:
            self.platforms_spawned = False
            self.punch_timer = 0; self.combo_count = 0; self.punch_anim = 0
            self.platforms = [[80, 360, 80, 150, 0, 150, 150, False], [352, 360, 80, 150, 0, 150, 150, False], [216, 240, 80, 150, 0, 150, 150, False]]
        elif self.level == 7: 
            self.by = FLOOR_Y - 80
            self.credits_y = 512

    def spawn_particles(self, x, y, color, count=5, custom_vx=None, custom_vy=None, gravity=False):
        for _ in range(count):
            vx = custom_vx if custom_vx is not None else random.uniform(-8, 8)
            vy = custom_vy if custom_vy is not None else random.uniform(-8, 8)
            life = random.randint(10, 25)
            self.particles.append(Particle(x, y, vx, vy, life, color, gravity))

    def spawn_explosion(self, x, y, max_radius, color_core, color_edge):
        self.explosions.append([x, y, 0, max_radius, color_core, color_edge])
        self.play_sfx(51) 
        
    def spawn_text(self, x, y, text, color):
        self.floating_texts.append(FloatingText(x, y, text, 30, color))
        
    def trigger_impact(self, shake_amt, flash=False):
        self.shake = max(self.shake, int(shake_amt * 1.0)) # Reduced screenshake by 50%
        if flash: self.flash = 2

    # ==========================================
    # --- UPDATE LOOPS ---
    # ==========================================
    def update(self):
        prev_hp = self.p_hp
        prev_coins = self.coins

        if self.shake > 0: self.shake -= 1
        if self.flash > 0: self.flash -= 1
        if self.p_attack_cd > 0: self.p_attack_cd -= 1
        if self.p_slow_timer > 0: self.p_slow_timer -= 1
        if getattr(self, 'flame_dmg_cd', 0) > 0: self.flame_dmg_cd -= 1

        if self.p_poisoned and pyxel.frame_count % 30 == 0:
            if not self.immortal: self.p_hp -= 2
            self.spawn_particles(self.px + 24, self.py + 32, 11, 2, gravity=False)
            self.spawn_text(self.px + 24, self.py - 10, "-2", 11)
            
        for r in self.rain:
            r[0] -= 3.0; r[1] += 10
            if r[1] > 512 or r[0] < 0:
                r[0] = random.randint(0, 700); r[1] = random.randint(-100, 0)
            elif r[1] > 320 and r[1] < 400 and random.random() < 0.05:
                self.water_splashes.append([r[0], r[1], 0])
                r[0] = random.randint(0, 700); r[1] = -20
                
        for l in self.leaves:
            l[0] -= 4 + math.sin(pyxel.frame_count * 0.05) * 3.0
            l[1] += 2.0 + math.cos(pyxel.frame_count * 0.1) * 2.0
            if l[0] < -20:
                l[0] = random.randint(520, 700); l[1] = random.randint(0, 320)

        for s in self.water_splashes[:]:
            s[2] += 0.2 
            if s[2] > 5.0: self.water_splashes.remove(s)

        for m in self.mist:
            m[0] -= m[2] + 1.0 
            if m[0] < -40: m[0] = SCREEN_WIDTH + random.randint(20, 100)

        self.bird["x"] += 3.0
        if self.bird["x"] > 520:
            self.bird["x"] = -random.randint(100, 400); self.bird["y"] = random.randint(60, 160); self.bird["swooping"] = False
        if not self.bird["swooping"] and 100 < self.bird["x"] < 300 and random.random() < 0.01:
            self.bird["swooping"] = True; self.bird["target_y"] = 320
        elif not self.bird["swooping"]:
            self.bird["target_y"] = 100 + math.sin(pyxel.frame_count * 0.05) * 20
        self.bird["y"] += (self.bird["target_y"] - self.bird["y"]) * 0.05
        if self.bird["swooping"] and abs(self.bird["y"] - 320) < 10:
            self.bird["swooping"] = False; self.water_splashes.append([self.bird["x"], 320, 0])

        self.p_hp_disp += (self.p_hp - self.p_hp_disp) * 0.1
        self.boss_hp_disp += (self.boss_hp - self.boss_hp_disp) * 0.1
        
        for p in self.particles[:]:
            p.update()
            if p.life <= 0: self.particles.remove(p)

        for e in self.explosions[:]:
            e[2] += 7.0 
            if e[2] >= e[3]: self.explosions.remove(e)
            
        for ft in self.floating_texts[:]:
            ft.update()
            if ft.life <= 0: self.floating_texts.remove(ft)

        for f in self.flames[:]:
            f[0] += f[2]; f[1] += f[3]; f[4] -= 1
            current_radius = max(4, f[4] // 2)
            if abs(f[0] - (self.px + 24)) < 20 + current_radius and abs(f[1] - (self.py + 32)) < 28 + current_radius:
                if not self.immortal and getattr(self, 'flame_dmg_cd', 0) <= 0: 
                    self.p_hp -= 5 
                    self.flame_dmg_cd = 15 
                    self.trigger_impact(8)
            if f[4] <= 0: self.flames.remove(f)

        if self.state == 7 or getattr(self, 'level', 1) == 7 and getattr(self, 'boss_hp', 1) <= 0:
            self.wind_timer += 0.05
            for ff in self.fireflies:
                ff[0] += math.sin(ff[2] * 0.1) * 1.0 + ff[3]
                ff[1] += math.cos(ff[2] * 0.1) * 0.6
                ff[2] += 1
                if ff[0] < -10 or ff[0] > 522: ff[3] *= -1
                
            for b in self.butterflies:
                b["x"] += b["vx"] + math.sin(pyxel.frame_count * 0.1) * 0.6
                b["y"] += b["vy"] + math.cos(pyxel.frame_count * 0.15) * 0.4
                b["flap_timer"] += 1
                if b["x"] < 20 or b["x"] > 492: b["vx"] *= -1
                if b["y"] < 260 or b["y"] > 460: b["vy"] *= -1
                if random.random() < 0.01:
                    b["vx"] = random.uniform(-2.4, 2.4); b["vy"] = random.uniform(-1.2, 1.2)
                    
            for b in self.bird_flock:
                b["x"] += b["speed"]
                if b["x"] > 600: b["x"] = random.randint(-300, -100)
                
            for leaf in self.autumn_leaves:
                leaf[0] -= leaf[2] * 1.5 + math.sin(leaf[1] * 0.05) * 1.0 
                leaf[1] += 1.6 
                if leaf[0] < -20 or leaf[1] > 520:
                    leaf[0] = random.randint(520, 600); leaf[1] = random.randint(-40, 400)

            self.blood_timer += 1
            if self.blood_timer > random.randint(45, 90):
                self.blood_timer = 0
                if random.random() < 0.7: 
                    drip_x = (256 - 24) + random.randint(8, 40); drip_y = (340 - 64) + random.randint(30, 60)
                    self.mage_blood_drips.append([drip_x, drip_y, 0, random.uniform(-0.2, 0.2)]) 
            
            for drip in self.mage_blood_drips[:]:
                drip[1] += drip[2]; drip[0] += drip[3]; drip[2] += GRAVITY * 0.4
                if drip[1] > 340: 
                    for _ in range(3):
                        self.particles.append(Particle(drip[0], drip[1]-2, random.uniform(-2, 2), random.uniform(-4, -1), 8, 8, True))
                    self.mage_blood_drips.remove(drip)
                    
            if getattr(self, 'dove', None):
                if self.dove["state"] == "flying":
                    target_x, target_y = 256 + 24, 244 
                    ang = math.atan2(target_y - self.dove["y"], target_x - self.dove["x"])
                    self.dove["x"] += math.cos(ang) * 3.0; self.dove["y"] += math.sin(ang) * 3.0
                    if abs(self.dove["x"] - target_x) < 6 and abs(self.dove["y"] - target_y) < 6:
                        self.dove["x"], self.dove["y"] = target_x, target_y
                        self.dove["state"] = "landed"

        # State Routing
        if self.state == 0: self.update_menu()
        elif self.state == 8: self.update_story()
        elif self.state == 1: self.update_upgrades()
        elif self.state == 2: self.update_dev()
        elif self.state == 6: self.update_settings()
        elif self.state == 3: self.update_play()
        elif self.state == 7: self.update_credits()
        elif self.state == 4: 
            if pyxel.btnp(pyxel.KEY_SPACE): self.state = 0
        elif self.state == 5: 
            if pyxel.btnp(pyxel.KEY_SPACE):
                if self.level == 7:
                    self.state = 7; self.credits_y = 512; self.play_music()
                else:
                    self.level += 1; self.reset_game(); self.state = 3

        if self.p_hp < prev_hp: self.play_sfx(54) 
        if self.coins > prev_coins: self.play_sfx(53) 

    def update_menu(self):
        if pyxel.btnp(pyxel.KEY_A): 
            # Reset dev panel buffs when starting a normal game
            self.dev_unlocked = False
            self.immortal = False
            self.dev_dmg_slider = 0.0
            
            if getattr(self, 'has_seen_story', False):
                self.level = 1; self.reset_game(); self.state = 3
            else:
                self.state = 8; self.story_timer = 0; self.play_music()
        if pyxel.btnp(pyxel.KEY_B): self.state = 1
        if pyxel.btnp(pyxel.KEY_C): self.dev_unlocked = True; self.state = 2
        if pyxel.btnp(pyxel.KEY_D): self.state = 6
        if pyxel.btnp(pyxel.KEY_E): self.level = 0; self.reset_game(); self.state = 3
        if pyxel.btnp(pyxel.KEY_F) and self.game_beaten:
            webbrowser.open("https://www.pyxelstudio.net/798rqnex")

    def update_story(self):
        self.story_timer += 1
        if self.story_timer > 1200 or pyxel.btnp(pyxel.KEY_SPACE):
            self.has_seen_story = True
            self.level = 1
            self.reset_game()
            self.state = 3

    def update_settings(self):
        if pyxel.btnp(pyxel.KEY_UP) or pyxel.btnp(pyxel.KEY_W):
            self.music_vol = min(7, self.music_vol + 1); self.init_audio(); self.play_music()
        if pyxel.btnp(pyxel.KEY_DOWN) or pyxel.btnp(pyxel.KEY_S):
            self.music_vol = max(0, self.music_vol - 1); self.init_audio(); self.play_music()
        if pyxel.btnp(pyxel.KEY_RIGHT) or pyxel.btnp(pyxel.KEY_D):
            self.sfx_vol = min(7, self.sfx_vol + 1); self.init_audio(); self.play_sfx(50)
        if pyxel.btnp(pyxel.KEY_LEFT) or pyxel.btnp(pyxel.KEY_A):
            self.sfx_vol = max(0, self.sfx_vol - 1); self.init_audio(); self.play_sfx(50)
        if pyxel.btnp(pyxel.KEY_BACKSPACE): self.state = 0

    def update_upgrades(self):
        dmg_cost = self.damage_lv * 50
        spd_cost = self.proj_speed_lv * 30
        hp_cost = self.hp_lv * 40
        
        if pyxel.btnp(pyxel.KEY_1) and self.coins >= dmg_cost:
            self.coins -= dmg_cost; self.damage_lv += 1; self.play_sfx(53)
        if pyxel.btnp(pyxel.KEY_2) and self.coins >= spd_cost:
            self.coins -= spd_cost; self.proj_speed_lv += 1; self.play_sfx(53)
        if pyxel.btnp(pyxel.KEY_3) and self.coins >= hp_cost:
            self.coins -= hp_cost; self.hp_lv += 1; self.max_hp += 20; self.p_hp += 20; self.play_sfx(53)
            
        def buy_or_equip(spell_name, cost):
            if spell_name in self.unlocked_spells:
                self.active_spell = spell_name; self.play_sfx(53)
            elif self.coins >= cost:
                self.coins -= cost; self.unlocked_spells.append(spell_name); self.active_spell = spell_name; self.play_sfx(53)

        if pyxel.btnp(pyxel.KEY_4): buy_or_equip("Teleport", 100)
        if pyxel.btnp(pyxel.KEY_5): buy_or_equip("Fireball", 150)
        if pyxel.btnp(pyxel.KEY_6): buy_or_equip("Freeze", 150)
        if pyxel.btnp(pyxel.KEY_7): buy_or_equip("Heal", 200)
        if pyxel.btnp(pyxel.KEY_BACKSPACE): self.state = 0

    def update_dev(self):
        mx, my = pyxel.mouse_x, pyxel.mouse_y
        if pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT):
            if 40 <= mx <= 240 and 320 <= my <= 350: self.slider_drag = True
            spells = ["Teleport", "Fireball", "Freeze", "Heal"]
            for i, sp in enumerate(spells):
                if 40 + i*90 <= mx <= 120 + i*90 and 380 <= my <= 410:
                    if sp not in self.unlocked_spells: self.unlocked_spells.append(sp)
                    self.active_spell = sp; self.play_sfx(53)
                
        if pyxel.btn(pyxel.MOUSE_BUTTON_LEFT) and self.slider_drag:
            self.dev_dmg_slider = max(0.0, min(1.0, (mx - 40) / 200.0))
        if pyxel.btnr(pyxel.MOUSE_BUTTON_LEFT): self.slider_drag = False

        for i in range(1, 8):
            if pyxel.btnp(getattr(pyxel, f"KEY_{i}")):
                self.level = i; self.immortal = True; self.reset_game(); self.state = 3

        if pyxel.btnp(pyxel.KEY_8):
            self.game_beaten = True
            self.play_sfx(53)

        if pyxel.btnp(pyxel.KEY_I): self.immortal = not self.immortal
        if pyxel.btnp(pyxel.KEY_BACKSPACE): self.state = 0
        
    def update_credits(self):
        self.credits_y -= 1.0 
        limit = -((len(self.credits_list) if self.credits_list else 100) * 80 + 400)
        if self.credits_y < limit or pyxel.btnp(pyxel.KEY_SPACE): self.state = 0

    def update_play(self):
        current_speed = PLAYER_SPEED
        if getattr(self, 'shake', 0) > 0 and self.level == 7: current_speed *= 0.5
        if self.p_slow_timer > 0: current_speed = 3.0
        if getattr(self, 'witch_darkness', False): current_speed *= 0.5
            
        in_web = False
        for w in self.web_traps[:]:
            w[2] -= 1
            if w[2] <= 0: self.web_traps.remove(w)
            elif abs((self.px + 24) - w[0]) < 70 and abs((self.py + 64) - w[1]) < 30:
                in_web = True
        
        if in_web: 
            self.p_slow_timer = 240 
            if not self.immortal and pyxel.frame_count % 30 == 0:
                self.p_hp -= 3; self.trigger_impact(4)
                
        if self.latched_spiders > 0: current_speed = max(1.0, current_speed - (self.latched_spiders * 0.6))

        if pyxel.btn(pyxel.KEY_A): self.px -= current_speed
        if pyxel.btn(pyxel.KEY_D): self.px += current_speed
        
        self.p_vy += GRAVITY
        self.py += self.p_vy
        on_ground = False

        for plat in getattr(self, 'platforms', []):
            while len(plat) < 8: plat.append(False) 
            
            px_center = self.px + 24
            if px_center > plat[0] and px_center < (plat[0] + plat[2]):
                plat[7] = True  
            else:
                plat[7] = False
                
            if plat[3] > 0: 
                if self.p_vy >= 0 and self.py + 64 >= plat[1] and self.py + 64 - self.p_vy <= plat[1] + 20:
                    if px_center > plat[0] and px_center < plat[0] + plat[2]:
                        self.py = plat[1] - 64; self.p_vy = 0; on_ground = True
                        
                if plat[7]: 
                    plat[3] -= 1 
                    if plat[3] % 30 == 0 or plat[3] < 90: 
                        self.spawn_particles(plat[0] + random.randint(0, plat[2]), plat[1] + 8, 13, 1, custom_vy=1.0)
            else: 
                plat[4] -= 1 
                if plat[4] <= 0:
                    plat[3] = plat[5]; plat[4] = plat[6]; plat[7] = False 
                    self.spawn_particles(plat[0] + plat[2]//2, plat[1] + 8, 10, 30)

        if self.py > FLOOR_Y - 64: 
            self.py = FLOOR_Y - 64; self.p_vy = 0; on_ground = True

        if pyxel.btnp(pyxel.KEY_SPACE) and on_ground:
            jump_pwr = JUMP_STRENGTH
            if getattr(self, 'witch_darkness', False): jump_pwr = -12 
            self.p_vy = jump_pwr
            self.play_sfx(52) 
            self.spawn_particles(self.px + 24, self.py + 64, 7, 16, gravity=True)

        if self.level == 0:
            if self.tut_step == 0:
                if pyxel.btn(pyxel.KEY_A) or pyxel.btn(pyxel.KEY_D): self.tut_timer += 1
                if self.tut_timer > 60: self.tut_step = 1; self.tut_timer = 0
            elif self.tut_step == 1:
                if self.boss_hp < self.boss_max_hp: self.tut_step = 2
            elif self.tut_step == 2:
                if "Fireball" not in self.unlocked_spells:
                    self.unlocked_spells.append("Fireball")
                    self.active_spell = "Fireball"
                if pyxel.btnp(pyxel.KEY_Q): self.tut_step = 3; self.tut_timer = 0
            elif self.tut_step == 3:
                self.tut_timer += 1
                if self.tut_timer > 120:
                    # Remove the temporary fireball
                    if "Fireball" in self.unlocked_spells:
                        self.unlocked_spells.remove("Fireball")
                    self.active_spell = None
                    self.level = 1; self.state = 0; return

        if pyxel.btn(pyxel.MOUSE_BUTTON_LEFT) and (self.p_attack_cd <= 0 or self.dev_unlocked):
            self.p_attack_cd = 12 if getattr(self, 'witch_darkness', False) else 6 
            self.shake = max(self.shake, 4) 
            dx = pyxel.mouse_x - (self.px + 24); dy = pyxel.mouse_y - (self.py + 32)
            angle = math.atan2(dy, dx)
            speed = 12 + (self.proj_speed_lv * 3.0)
            self.projectiles.append([self.px + 24, self.py + 32, math.cos(angle) * speed, math.sin(angle) * speed, "basic"])
            self.spawn_particles(self.px + 24, self.py + 32, 11, 8, -math.cos(angle)*6, -math.sin(angle)*6)
            self.play_sfx(50) 
            if self.latched_spiders > 0:
                self.spawn_particles(self.px + 24, self.py + 32, 3, self.latched_spiders*8)
                self.spawn_text(self.px, self.py - 40, "CLEARED", 3)
                self.latched_spiders = 0

        if pyxel.btnp(pyxel.KEY_Q) and self.spell_cooldown <= 0 and self.active_spell:
            if self.active_spell == "Teleport":
                self.spawn_explosion(self.px + 24, self.py + 32, 50, 12, 1)
                self.px += 120 if pyxel.btn(pyxel.KEY_D) else -120
                self.spawn_explosion(self.px + 24, self.py + 32, 50, 7, 12)
                self.trigger_impact(8); self.spell_cooldown = 60; self.max_cd = 60; self.play_sfx(50)
                
            elif self.active_spell == "Fireball":
                dx = pyxel.mouse_x - (self.px + 24); dy = pyxel.mouse_y - (self.py + 32)
                angle = math.atan2(dy, dx)
                self.projectiles.append([self.px + 24, self.py + 32, math.cos(angle) * 16, math.sin(angle) * 16, "fireball"])
                self.trigger_impact(8); self.spell_cooldown = 120; self.max_cd = 120; self.play_sfx(50)
                
            elif self.active_spell == "Freeze":
                dx = pyxel.mouse_x - (self.px + 24); dy = pyxel.mouse_y - (self.py + 32)
                angle = math.atan2(dy, dx)
                self.projectiles.append([self.px + 24, self.py + 32, math.cos(angle) * 20, math.sin(angle) * 20, "freeze"])
                self.spell_cooldown = 90; self.max_cd = 90; self.play_sfx(50)
                
            elif self.active_spell == "Heal":
                heal_amt = int(self.max_hp * 0.35) 
                self.p_hp = min(self.max_hp, self.p_hp + heal_amt)
                self.spawn_particles(self.px + 24, self.py + 32, 11, 40, gravity=False)
                self.spawn_text(self.px + 24, self.py - 20, f"+{heal_amt}", 11)
                self.trigger_impact(4, flash=True)
                self.spell_cooldown = 270; self.max_cd = 270 
                self.play_sfx(53)
                
        if self.spell_cooldown > 0: self.spell_cooldown -= 1

        mult = 1.0 + (self.dev_dmg_slider * 99) if self.dev_unlocked else 1.0
        base_dmg = int((2 * self.damage_lv) * mult)
        
        if self.boss_on_fire > 0:
            self.boss_on_fire -= 1
            if pyxel.frame_count % 15 == 0:
                tick_dmg = int((self.damage_lv * 2) * mult)
                self.boss_hp -= tick_dmg
                self.spawn_text(self.bx, self.by - 60, str(tick_dmg), 8)
                self.spawn_particles(self.bx, self.by - 40, 9, 6, gravity=True)
        
        for p in self.projectiles[:]:
            p[0] += p[2]; p[1] += p[3]
            
            if len(p) > 4 and p[4] == "fireball":
                bw = 40 if self.level in [1, 6, 0] else (80 if self.level == 4 else (32 if self.level == 7 else 48))
                bh = 40 if self.level == 1 else (80 if self.level in [4, 6, 0] else (80 if self.level == 7 else 90))
                by_off = self.by if self.level in [1, 0] else self.by - 50
                
                hit = False
                if p[1] >= FLOOR_Y: hit = True
                elif not self.boss_immune and abs(p[0] - self.bx) < bw and abs(p[1] - by_off) < bh: hit = True
                        
                if not hit:
                    for s in self.squires:
                        if abs(p[0] - s[0]) < 32 and abs(p[1] - s[1]) < 32: hit = True; break
                    for sk in self.skeletons:
                        if abs(p[0] - sk[0]) < 32 and abs(p[1] - sk[1]) < 40: hit = True; break
                
                if hit:
                    actual_dmg = base_dmg * 2
                    self.spawn_explosion(p[0], p[1], 90, 9, 8) 
                    self.spawn_particles(p[0], p[1], 10, 40, custom_vx=random.uniform(-10,10), custom_vy=random.uniform(-10,10))
                    self.spawn_particles(p[0], p[1], 9, 30, gravity=True)
                    self.trigger_impact(24, flash=True); self.play_sfx(63) 
                    
                    if not self.boss_immune and abs(p[0] - self.bx) < 120 and abs(p[1] - by_off) < 120:
                        self.boss_hp -= actual_dmg; self.boss_on_fire = 150
                        self.spawn_text(self.bx, by_off - 40, actual_dmg, 8)
                                  
                    for s in self.squires[:]:
                        if abs(p[0] - s[0]) < 100 and abs(p[1] - s[1]) < 100:
                            if len(s) > 2:
                                if s[3] == 2 and s[6] > 0:
                                    s[6] -= 5; self.play_sfx(60); self.spawn_text(s[0], s[1] - 30, "BLOCKED", 6)
                                else:
                                    s[2] -= actual_dmg
                                    if s[2] <= 0:
                                        if s in self.squires: self.squires.remove(s)
                                        if self.level != 0: self.coins += 5
                                        self.spawn_explosion(s[0], s[1], 50, 9, 10)
                            else:
                                if s in self.squires: self.squires.remove(s)
                                if self.level != 0: self.coins += 2
                                self.spawn_explosion(s[0], s[1], 50, 9, 10)
                                
                    for sk in self.skeletons[:]:
                        if abs(p[0] - sk[0]) < 100 and abs(p[1] - sk[1]) < 100:
                            sk[2] -= actual_dmg
                            if sk[2] <= 0:
                                if sk in self.skeletons: self.skeletons.remove(sk)
                                self.skeletons_killed += 1
                                self.spawn_explosion(sk[0], sk[1], 50, 7, 13)
                                
                    for ts in self.tiny_spiders[:]:
                        if abs(p[0] - ts[0]) < 100 and abs(p[1] - ts[1]) < 100:
                            if ts in self.tiny_spiders: self.tiny_spiders.remove(ts)
                            self.spawn_particles(ts[0], ts[1], 11, 30, gravity=True)
                            
                    if p in self.projectiles: self.projectiles.remove(p)
                continue 

            bw = 40 if self.level in [1, 6, 0] else (80 if self.level == 4 else (32 if self.level == 7 else 48))
            bh = 40 if self.level == 1 else (80 if self.level in [4, 6, 0] else (80 if self.level == 7 else 90))
            by_off = self.by if self.level in [1, 0] else self.by - 50
            
            p_destroyed = False
            
            if not self.boss_immune and abs(p[0] - self.bx) < bw and abs(p[1] - by_off) < bh:
                actual_dmg = base_dmg
                if p[4] == "freeze":
                    actual_dmg = int(base_dmg * 0.5)
                    self.boss_freeze_stacks += 1
                    if self.boss_freeze_stacks >= 3:
                        self.boss_frozen_timer = 150 
                        self.boss_freeze_stacks = 0
                        self.spawn_explosion(p[0], p[1], 80, 12, 7) 
                        self.trigger_impact(16, flash=True)
                    else:
                        self.spawn_particles(p[0], p[1], 12, 20)
                
                self.boss_hp -= actual_dmg
                if self.level != 0: self.coins += 1
                self.play_sfx(55) 
                self.spawn_particles(p[0], p[1], 8, 30, gravity=True) 
                self.spawn_text(p[0], p[1] - 40, actual_dmg, 10 if mult > 2 else 7)
                self.trigger_impact(8)
                p_destroyed = True

            if not p_destroyed:
                for ts in self.tiny_spiders[:]:
                    if abs(p[0] - ts[0]) < 24 and abs(p[1] - ts[1]) < 24: 
                        if ts in self.tiny_spiders: self.tiny_spiders.remove(ts)
                        self.play_sfx(55); self.spawn_particles(ts[0], ts[1], 11, 30, gravity=True) 
                        self.shake = max(self.shake, 2); p_destroyed = True; break

            if not p_destroyed:
                for sk in self.skeletons[:]:
                    if abs(p[0] - sk[0]) < 24 and abs(p[1] - sk[1]) < 40:
                        sk[2] -= base_dmg; self.spawn_particles(p[0], p[1], 7, 10); self.play_sfx(55)
                        if sk[2] <= 0:
                            if sk in self.skeletons: self.skeletons.remove(sk)
                            self.skeletons_killed += 1
                            self.spawn_explosion(sk[0], sk[1], 50, 7, 13)
                        p_destroyed = True; break

            if not p_destroyed:
                for s in self.squires[:]:
                    sx, sy = s[0], s[1]
                    hit = False
                    if len(s) > 2: 
                        if abs(p[0] - sx) < 24 and abs(p[1] - sy) < 32:
                            if s[3] == 2 and s[6] > 0: 
                                if (p[2] > 0 and s[4] == -1) or (p[2] < 0 and s[4] == 1):
                                    s[6] -= 1; self.play_sfx(60); self.spawn_particles(p[0], p[1], 7, 10)
                                    self.spawn_text(p[0], p[1] - 20, "BLOCKED", 6); p_destroyed = True; break
                            s[2] -= base_dmg; hit = True; self.play_sfx(55)
                            if s[2] <= 0:
                                if s in self.squires: self.squires.remove(s)
                                if self.level != 0: self.coins += 5
                                self.spawn_explosion(sx, sy, 50, 9, 10); self.trigger_impact(16, flash=True)
                            p_destroyed = True
                    else: 
                        if abs(p[0] - sx) < 24 and abs(p[1] - sy) < 24:
                            if s in self.squires: self.squires.remove(s)
                            if self.level != 0: self.coins += 2
                            self.spawn_explosion(sx, sy, 50, 9, 10); self.trigger_impact(16, flash=True)
                            p_destroyed = True
                    if hit: break
            
            if p[0] < 0 or p[0] > SCREEN_WIDTH or p[1] < 0 or p[1] > SCREEN_HEIGHT: p_destroyed = True
            if p_destroyed and p in self.projectiles: self.projectiles.remove(p)

        for w in self.geyser_warns[:]:
            w[1] -= 1
            if w[1] <= 0:
                self.geysers.append([w[0], 30]); self.geyser_warns.remove(w)
                self.play_sfx(62); self.trigger_impact(20)
        for g in self.geysers[:]:
            g[1] -= 1
            if abs((self.px + 24) - g[0]) < 40:
                if not self.immortal and pyxel.frame_count % 5 == 0:
                    self.p_hp -= 4; self.trigger_impact(8)
            if g[1] <= 0: self.geysers.remove(g)

        if self.boss_frozen_timer > 0:
            self.boss_frozen_timer -= 1
            if pyxel.frame_count % 5 == 0: self.spawn_particles(self.bx, self.by - 40, 12, 4, gravity=True)
                
        elif self.level != 0:
            hp_pct = self.boss_hp / self.boss_max_hp
            
            if self.level == 1:
                self.boss_timer += 1
                if pyxel.frame_count % 30 == 0: self.b_target_x = random.randint(80, 420)
                speed = 2.0
                if getattr(self, 'b_target_x', 400):
                    if self.bx < self.b_target_x - 10: self.bx += speed; self.b_dir = 1
                    elif self.bx > self.b_target_x + 10: self.bx -= speed; self.b_dir = -1
                
                if hp_pct < 0.3 and not self.squires_spawned:
                    # Blue squires (type 0)
                    self.boss_immune = True; self.squires = [[80, 160, 8, 0, 1, 0, 15], [256, 160, 8, 0, 1, 0, 15], [420, 160, 8, 0, -1, 0, 15]]
                    self.squires_spawned = True; self.trigger_impact(40, flash=True) 
                if len(self.squires) == 0: self.boss_immune = False
                
                attack_rate = max(10, 40 - (self.level * 5))
                if self.boss_timer % attack_rate == 0:
                    if hp_pct > 0.6: self.bombs.append([self.bx, self.by + 20, 0])
                    elif hp_pct > 0.3:
                        for angle in [-0.5, 0, 0.5]: self.bombs.append([self.bx, self.by + 20, angle])
                
                for b in self.bombs[:]:
                    b[1] += 6; b[0] += b[2]*6
                    self.spawn_particles(b[0], b[1], 7, 4) 
                    if abs(b[0] - (self.px + 24)) < 24 and abs(b[1] - (self.py + 32)) < 24:
                        if not self.immortal: self.p_hp -= 10
                        self.spawn_explosion(b[0], b[1], 70, 8, 9); self.bombs.remove(b)
                        self.trigger_impact(24, flash=True)
                    elif b[1] > FLOOR_Y:
                        self.spawn_explosion(b[0], b[1], 50, 7, 8); self.bombs.remove(b)
                        self.trigger_impact(8)
                        
            elif self.level == 2:
                rage = hp_pct < 0.5
                action_speed = 1.5 if rage else 1.0
                
                if self.boss_action == 0: 
                    speed = 4.0 if rage else 2.0
                    if self.bx < self.px - 80: self.bx += speed; self.b_dir = 1
                    elif self.bx > self.px + 80: self.bx -= speed; self.b_dir = -1
                    
                    self.boss_timer += action_speed
                    if self.boss_timer > 50:
                        self.boss_timer = 0
                        self.swing_angle = math.pi if self.b_dir == 1 else 0 
                        if rage and random.random() < 0.5: self.boss_action = 3 
                        else: self.boss_action = 1 
                        
                elif self.boss_action == 1: 
                    self.boss_timer += action_speed
                    self.swing_angle += (0.05 * action_speed * self.b_dir)
                    if self.boss_timer > 25:
                        self.boss_timer = 0; self.boss_action = 2; self.play_sfx(57) 
                        
                elif self.boss_action == 2: 
                    self.boss_timer += action_speed
                    arc = (math.pi * 1.2) * (self.boss_timer / 15)
                    self.swing_angle = (math.pi if self.b_dir == -1 else 0) + (arc * self.b_dir)
                    
                    if self.boss_timer > 5 and self.boss_timer < 10: 
                        hit_dist = abs((self.px + 24) - self.bx)
                        if hit_dist < 160 and self.py > FLOOR_Y - 100:
                            if not self.immortal: self.p_hp -= (30 if rage else 20)
                            self.trigger_impact(40, flash=True)
                            self.spawn_explosion(self.px + 24, self.py + 32, 70, 8, 2)
                            self.boss_timer = 15 
                            
                    if self.boss_timer > 15: self.boss_timer = 0; self.boss_action = 0
                        
                elif self.boss_action == 3: 
                    self.boss_timer += action_speed
                    if pyxel.frame_count % 2 == 0: 
                        self.spawn_particles(self.bx + (24*self.b_dir), self.by - 80, 9, 10, gravity=True)
                    if self.boss_timer > 30:
                        self.boss_timer = 0; self.boss_action = 4; self.trigger_impact(16)
                        
                elif self.boss_action == 4: 
                    self.boss_timer += action_speed
                    self.shake = max(self.shake, 12) 
                    breath_x, breath_y = self.bx, self.by - 92 
                    target_x, target_y = self.px + 24, self.py + 32
                    aim_angle = math.atan2(target_y - breath_y, target_x - breath_x)
                    
                    for _ in range(50 if rage else 30):
                        vx = math.cos(aim_angle + random.uniform(-0.35, 0.35)) * random.uniform(10, 24)
                        vy = math.sin(aim_angle + random.uniform(-0.35, 0.35)) * random.uniform(10, 24)
                        self.flames.append([breath_x, breath_y, vx, vy, random.randint(30, 55)])
                                
                    if self.boss_timer > (50 if rage else 70):
                        self.boss_timer = 0; self.boss_action = 0

            elif self.level == 3:
                rage = hp_pct < 0.4
                target_x, target_y = self.px + 24, self.py + 32
                gun_x, gun_y = self.bx, self.by - 64
                self.boss_aim_angle = math.atan2(target_y - gun_y, target_x - gun_x)
                self.b_dir = 1 if self.px > self.bx else -1

                if self.boss_action == 0: 
                    speed = 2.4
                    if self.bx < self.px - 120: self.bx += speed
                    elif self.bx > self.px + 120: self.bx -= speed
                    self.boss_timer += 1
                    if self.boss_timer > 40:
                        self.boss_timer = 0; self.boss_action = 2 if rage else 1
                        
                elif self.boss_action == 1: 
                    self.boss_timer += 1
                    if self.boss_timer == 10:
                        self.trigger_impact(24, flash=True)
                        self.spawn_explosion(gun_x + math.cos(self.boss_aim_angle)*40, gun_y + math.sin(self.boss_aim_angle)*40, 50, 10, 9)
                        for a_off in [-0.3, -0.15, 0, 0.15, 0.3]:
                            aim = self.boss_aim_angle + a_off
                            self.boss_bullets.append([gun_x, gun_y, math.cos(aim)*16, math.sin(aim)*16, "shotgun"])
                    if self.boss_timer > 40: self.boss_timer = 0; self.boss_action = 0
                        
                elif self.boss_action == 2: 
                    self.boss_timer += 1
                    if self.boss_timer % 3 == 0:
                        self.shake = max(self.shake, 8)
                        a = self.boss_aim_angle + random.uniform(-0.1, 0.1)
                        self.boss_bullets.append([gun_x, gun_y, math.cos(a)*12, math.sin(a)*12, "mini"])
                    if self.boss_timer > 60: self.boss_timer = 0; self.boss_action = 0

                for b in self.boss_bullets[:]:
                    b[0] += b[2]; b[1] += b[3]
                    if abs(b[0] - (self.px + 24)) < 20 and abs(b[1] - (self.py + 32)) < 20:
                        if not self.immortal: self.p_hp -= (15 if b[4] == "shotgun" else 4)
                        self.trigger_impact(16); self.spawn_explosion(self.px + 24, self.py + 32, 40, 8, 2)
                        if b in self.boss_bullets: self.boss_bullets.remove(b)
                        continue

                    for plat in getattr(self, 'platforms', []):
                        if plat[3] > 0: 
                            if plat[0] <= b[0] <= plat[0] + plat[2] and plat[1] <= b[1] <= plat[1] + 20:
                                plat[3] -= (5 if b[4] == "shotgun" else 1) 
                                self.spawn_particles(b[0], b[1], 13, 6, gravity=True)
                                if b in self.boss_bullets: self.boss_bullets.remove(b)
                                break
                                
                    if b[0] < -100 or b[0] > SCREEN_WIDTH+100 or b[1] < -100 or b[1] > SCREEN_HEIGHT:
                        if b in self.boss_bullets: self.boss_bullets.remove(b)

            elif self.level == 4:
                self.boss_vy += GRAVITY
                self.by += self.boss_vy
                if self.by > FLOOR_Y: self.by = FLOOR_Y; self.boss_vy = 0
                    
                if hp_pct > 0.8: 
                    self.boss_timer += 1
                    if self.boss_timer > 60:
                        self.b_dir = 1 if self.bx < 256 else -1
                        self.boss_timer = 0
                    self.bx += 8.0 * self.b_dir
                    if self.bx < 40: self.b_dir = 1
                    if self.bx > 472: self.b_dir = -1
                    if pyxel.frame_count % 30 == 0: self.play_sfx(62) 
                    if random.random() < 0.02 and self.by == FLOOR_Y: self.boss_vy = -16 
                    if abs(self.bx - (self.px + 24)) < 80 and abs((self.by - 40) - (self.py + 32)) < 60:
                        if not self.immortal and pyxel.frame_count % 10 == 0:
                            self.p_hp -= 15; self.trigger_impact(12)
                
                elif hp_pct > 0.4 and not self.squires_spawned: 
                    self.boss_immune = True; self.squires_spawned = True; self.boss_action = 1 
                    for _ in range(9):
                        sx = random.randint(40, 472)
                        self.squires.append([sx, FLOOR_Y, 8, random.randint(0, 2), 1 if sx < 256 else -1, 0, 15]) 
                    self.trigger_impact(40, flash=True); self.play_sfx(56) 
                    
                elif hp_pct > 0.4 and self.squires_spawned: 
                    if self.bx < 240: self.bx += 2
                    elif self.bx > 272: self.bx -= 2
                    
                    for s in self.squires:
                        s[4] = 1 if s[0] < self.px else -1
                        if s[3] == 0: 
                            s[0] += 3.0 * s[4]
                            if abs(s[0] - (self.px + 24)) < 32 and abs(s[1] - (self.py + 64)) < 32:
                                if not self.immortal and pyxel.frame_count % 15 == 0:
                                    self.p_hp -= 10; self.trigger_impact(8)
                        elif s[3] == 1: 
                            s[5] += 1
                            if s[5] > 90:
                                s[5] = 0
                                angle = math.atan2(self.py + 32 - s[1], self.px + 24 - s[0])
                                self.boss_bullets.append([s[0], s[1] - 32, math.cos(angle)*10, math.sin(angle)*10, "mini"])
                        elif s[3] == 2: 
                            s[0] += 1.0 * s[4]
                            if abs(s[0] - (self.px + 24)) < 32 and abs(s[1] - (self.py + 64)) < 32:
                                if not self.immortal and pyxel.frame_count % 30 == 0:
                                    self.p_hp -= 5; self.trigger_impact(4)
                                    
                    if len(self.squires) == 0: 
                        self.boss_immune = False; self.boss_action = 2 
                        
                    if self.boss_action == 2: 
                        self.boss_timer += 1
                        if self.boss_timer > 45:
                            self.boss_timer = 0
                            angle = math.atan2(self.py + 32 - (self.by - 40), self.px + 24 - self.bx)
                            self.boss_bullets.append([self.bx, self.by - 40, math.cos(angle)*12, math.sin(angle)*12, "burger"])
                
                else: 
                    if self.lava_timer < 330: 
                        self.boss_immune = True; self.lava_timer += 1
                        if self.lava_timer % 20 == 0: 
                            self.geyser_warns.append([self.px + random.randint(-120, 120), 80]) 
                    else: 
                        self.boss_immune = False; self.boss_action = 3; self.boss_timer += 1
                        if self.boss_timer > 20: 
                            self.boss_timer = 0
                            angle = math.atan2(self.py + 32 - (self.by - 40), self.px + 24 - self.bx)
                            self.boss_bullets.append([self.bx, self.by - 40, math.cos(angle)*16, math.sin(angle)*16, "burger"])

                for b in self.boss_bullets[:]:
                    b[0] += b[2]; b[1] += b[3]
                    if b[4] == "burger":
                        b[3] += GRAVITY * 0.5 
                        if b[1] > FLOOR_Y: b[1] = FLOOR_Y; b[3] = -abs(b[3]) * 0.8
                    if abs(b[0] - (self.px + 24)) < 24 and abs(b[1] - (self.py + 32)) < 24:
                        if not self.immortal: self.p_hp -= 15
                        self.trigger_impact(16); self.spawn_explosion(self.px + 24, self.py + 32, 40, 8, 2)
                        if b in self.boss_bullets: self.boss_bullets.remove(b)
                        continue
                    if b[0] < -100 or b[0] > SCREEN_WIDTH + 100 or b[1] < -100 or b[1] > SCREEN_HEIGHT:
                        if b in self.boss_bullets: self.boss_bullets.remove(b)

            elif self.level == 5:
                if hp_pct <= 0.3 and not self.exploded:
                    self.exploded = True; self.boss_action = 4 
                    self.boss_hp = self.boss_max_hp * 0.1 
                    self.spawn_explosion(self.bx, self.by - 40, 200, 7, 13)
                    self.trigger_impact(50, flash=True); self.play_sfx(56)
                    if abs((self.px + 24) - self.bx) < 200 and abs((self.py + 32) - (self.by - 40)) < 200:
                        if not self.immortal: self.p_hp -= 40 
                
                elif hp_pct <= 0.6 and hp_pct > 0.4 and self.boss_action == 0:
                    self.boss_action = 1 
                    self.boss_immune = True
                    self.swarm_timer = 0
                    self.tiny_spiders.clear()
                    self.latched_spiders = 0
                    
                elif self.boss_action == 1 and self.swarm_timer > 300: 
                    self.boss_action = 2 
                    self.boss_immune = False; self.by = -100; self.bx = 256; self.boss_vy = 0

                if self.boss_action == 0: 
                    self.boss_vy += GRAVITY; self.by += self.boss_vy
                    if self.by > FLOOR_Y: self.by = FLOOR_Y; self.boss_vy = 0
                    if self.bx < self.px: self.bx += 2; self.b_dir = 1
                    elif self.bx > self.px + 48: self.bx -= 2; self.b_dir = -1
                    if random.random() < 0.03 and self.by == FLOOR_Y: self.boss_vy = -24
                    self.boss_timer += 1
                    if self.boss_timer > 50:
                        self.boss_timer = 0
                        angle = math.atan2(self.py + 32 - (self.by - 40), self.px + 24 - self.bx)
                        self.boss_bullets.append([self.bx, self.by - 40, math.cos(angle)*12, math.sin(angle)*12, "web"])
                        
                elif self.boss_action == 1: 
                    self.swarm_timer += 1
                    if pyxel.frame_count % 2 == 0:
                        self.tiny_spiders.append([random.randint(0, 512), -20])
                        if random.random() < 0.5: self.tiny_spiders.append([random.randint(0, 512), -40])
                        
                    if self.latched_spiders > 0 and pyxel.frame_count % 30 == 0:
                        if not self.immortal: self.p_hp -= (self.latched_spiders * 2)
                        self.trigger_impact(4)
                        
                elif self.boss_action == 2: 
                    self.boss_vy += GRAVITY; self.by += self.boss_vy
                    if self.by > FLOOR_Y: 
                        if self.boss_vy > 10: self.trigger_impact(20)
                        self.by = FLOOR_Y; self.boss_vy = 0
                    
                    if self.bx < self.px: self.bx += 4; self.b_dir = 1
                    elif self.bx > self.px + 48: self.bx -= 4; self.b_dir = -1
                    
                    if abs(self.bx - (self.px + 24)) < 60 and abs((self.by - 40) - (self.py + 32)) < 60:
                        if not self.immortal and pyxel.frame_count % 15 == 0:
                            self.p_hp -= 15; self.trigger_impact(10)
                            
                    self.boss_timer += 1
                    if self.boss_timer > 30:
                        self.boss_timer = 0
                        angle = math.atan2(self.py + 32 - (self.by - 40), self.px + 24 - self.bx)
                        self.boss_bullets.append([self.bx, self.by - 40, math.cos(angle)*16, math.sin(angle)*16, "web"])

                target_x, target_y = self.px + 24, self.py + 32
                for ts in self.tiny_spiders[:]:
                    angle = math.atan2(target_y - ts[1], target_x - ts[0])
                    ts[0] += math.cos(angle) * 4.0; ts[1] += math.sin(angle) * 4.0
                    if abs(ts[0] - target_x) < 30 and abs(ts[1] - target_y) < 30:
                        self.latched_spiders += 1
                        self.spawn_particles(ts[0], ts[1], 8, 4); self.tiny_spiders.remove(ts)

                for b in self.boss_bullets[:]:
                    b[0] += b[2]; b[1] += b[3]
                    if b[4] == "web":
                        if b[1] >= FLOOR_Y:
                            self.web_traps.append([b[0], FLOOR_Y, 240]) 
                            self.play_sfx(62) 
                            if b in self.boss_bullets: self.boss_bullets.remove(b)
                            continue
                        
                        if abs(b[0] - (self.px + 24)) < 24 and abs(b[1] - (self.py + 32)) < 24:
                            if not self.immortal: self.p_hp -= 10
                            self.trigger_impact(10)
                            if b in self.boss_bullets: self.boss_bullets.remove(b)
                    elif b[0] < -100 or b[0] > SCREEN_WIDTH + 100 or b[1] < -100 or b[1] > SCREEN_HEIGHT:
                        if b in self.boss_bullets: self.boss_bullets.remove(b)

            elif self.level == 6:
                l6_rage = hp_pct <= 0.8
                l6_balkan = hp_pct <= 0.2

                self.boss_vy += GRAVITY
                self.by += self.boss_vy
                on_b_ground = False

                if l6_rage and not self.platforms_spawned:
                    self.platforms = [[80, 360, 80, 150, 0, 150, 150, False], [352, 360, 80, 150, 0, 150, 150, False], [216, 240, 80, 150, 0, 150, 150, False]]
                    self.platforms_spawned = True
                    self.trigger_impact(40, flash=True)
                    self.spawn_explosion(self.bx, self.by - 40, 120, 8, 2)
                    self.play_sfx(56)

                for plat in self.platforms:
                    if len(plat) > 3 and plat[3] > 0:
                        if self.boss_vy >= 0 and self.by >= plat[1] and self.by - self.boss_vy <= plat[1] + 20:
                            if self.bx + 28 > plat[0] and self.bx - 28 < plat[0] + plat[2]:
                                self.by = plat[1]; self.boss_vy = 0; on_b_ground = True

                if self.by > FLOOR_Y:
                    self.by = FLOOR_Y; self.boss_vy = 0; on_b_ground = True

                dist_x = (self.px + 24) - self.bx; dist_y = (self.py + 32) - self.by
                self.b_dir = 1 if dist_x > 0 else -1

                if l6_balkan: speed = 2.4
                elif l6_rage: speed = 6.2
                else: speed = 4.8

                if abs(dist_x) > 30: self.bx += speed * self.b_dir

                if l6_rage and dist_y < -40 and on_b_ground and random.random() < 0.1:
                    self.boss_vy = JUMP_STRENGTH * 1.2
                    self.spawn_particles(self.bx, self.by, 8, 10, gravity=True)

                if self.punch_anim > 0: self.punch_anim -= 1

                if self.punch_timer > 0:
                    self.punch_timer -= 1
                else:
                    if abs(dist_x) < 70 and abs(dist_y) < 80:
                        self.punch_anim = 10 
                        if l6_balkan:
                            self.punch_timer = random.randint(30, 60)
                            if not self.immortal: self.p_hp -= 25 
                            self.trigger_impact(30)
                            self.spawn_explosion(self.px + 24, self.py + 32, 40, 8, 2); self.play_sfx(57)
                        elif l6_rage:
                            self.combo_count += 1
                            if self.combo_count < 6:
                                self.punch_timer = 8
                                if not self.immortal: self.p_hp -= 5 
                                self.trigger_impact(6)
                            else:
                                self.punch_timer = 40; self.combo_count = 0
                                if not self.immortal: self.p_hp -= 15 
                                self.trigger_impact(24)
                                self.spawn_explosion(self.px + 24, self.py + 32, 60, 8, 10); self.play_sfx(57)
                                self.px += 80 * self.b_dir 
                        else:
                            self.punch_timer = 12
                            if not self.immortal: self.p_hp -= 8 
                            self.trigger_impact(8)

            elif self.level == 7:
                if hp_pct <= 0.1 and self.boss_action != 3:
                    self.boss_action = 3; self.boss_immune = False
                    self.spawn_explosion(self.bx, self.by - 40, 120, 8, 2) 
                    self.trigger_impact(40, flash=True); self.play_sfx(58) 
                elif hp_pct <= 0.65 and hp_pct > 0.1 and self.boss_action == 0:
                    self.boss_action = 1; self.boss_immune = True; self.witch_darkness = True
                    pyxel.stop(0); pyxel.stop(1); pyxel.stop(2) 
                    self.by = -2000  
                    self.skeletons_killed = 0; self.skeletons_to_spawn = 20 
                    self.trigger_impact(60); self.play_sfx(56)
                    self.spawn_text(256, 240, "DOOM AND DESPAIR", 8)
                
                for sk in self.skeletons[:]:
                    if sk[0] < -200 or sk[0] > SCREEN_WIDTH + 200:
                        self.skeletons.remove(sk)
                        continue
                    
                    sk_dist_x = (self.px + 24) - sk[0]
                    sk[3] = 1 if sk_dist_x > 0 else -1
                    sk[0] += 2.0 * sk[3]
                    if abs(sk_dist_x) < 30 and abs((self.py + 32) - sk[1]) < 40:
                        if not self.immortal and pyxel.frame_count % 30 == 0:
                            self.p_hp -= 10; self.trigger_impact(10)
                            
                for sp in self.earth_spikes[:]:
                    sp[1] -= 1
                    if sp[1] <= 0 and sp[2] == 0:
                        sp[2] = 1; sp[1] = 20 
                        self.trigger_impact(16); self.play_sfx(62)
                        self.spawn_particles(sp[0], FLOOR_Y, 4, 20, gravity=True)
                    elif sp[2] == 1:
                        if abs((self.px + 24) - sp[0]) < 50 and abs((self.py + 64) - FLOOR_Y) < 60: 
                            if not self.immortal and pyxel.frame_count % 10 == 0:
                                self.p_hp -= 15; self.trigger_impact(8)
                        if sp[1] <= 0: self.earth_spikes.remove(sp)

                target_x, target_y = self.px + 24, self.py + 32
                for sh in self.soul_heads[:]:
                    sh[4] -= 1 
                    angle = math.atan2(target_y - sh[1], target_x - sh[0])
                    sh[2] += math.cos(angle) * 0.3; sh[3] += math.sin(angle) * 0.3
                    spd = math.hypot(sh[2], sh[3])
                    if spd > 9.0: sh[2] = (sh[2]/spd)*9.0; sh[3] = (sh[3]/spd)*9.0
                    
                    sh[0] += sh[2]; sh[1] += sh[3]
                    if abs(sh[0] - target_x) < 20 and abs(sh[1] - target_y) < 20:
                        if not self.immortal: self.p_hp -= 12
                        self.trigger_impact(12); self.spawn_explosion(sh[0], sh[1], 40, 11, 3); self.soul_heads.remove(sh)
                    elif sh[4] <= 0:
                        self.spawn_particles(sh[0], sh[1], 13, 10); self.soul_heads.remove(sh)
                        
                for b in self.boss_bullets[:]:
                    b[0] += b[2]; b[1] += b[3]
                    if b[4] in ["witch_fireball", "witch_freeze"]:
                        if abs(b[0] - (self.px + 24)) < 24 and abs(b[1] - (self.py + 32)) < 24:
                            if not self.immortal: self.p_hp -= (20 if b[4] == "witch_fireball" else 10)
                            self.trigger_impact(16); self.spawn_explosion(b[0], b[1], 50, 8 if b[4] == "witch_fireball" else 12, 2)
                            if b in self.boss_bullets: self.boss_bullets.remove(b)
                            continue
                        if b[0] < -100 or b[0] > SCREEN_WIDTH + 100 or b[1] < -100 or b[1] > SCREEN_HEIGHT:
                            if b in self.boss_bullets: self.boss_bullets.remove(b)
                    elif b[4] == "poison":
                        if abs(b[0] - (self.px + 24)) < 30 and abs(b[1] - (self.py + 32)) < 30:
                            self.p_poisoned = True
                            self.spawn_explosion(b[0], b[1], 60, 11, 3)
                            if b in self.boss_bullets: self.boss_bullets.remove(b)
                            continue

                if self.boss_action == 0:
                    self.boss_timer += 1
                    self.bx += math.sin(pyxel.frame_count * 0.05) * 3.0
                    self.by = FLOOR_Y - 80 + math.sin(pyxel.frame_count * 0.1) * 10
                    
                    if self.boss_timer > 30:
                        self.boss_timer = 0
                        attack = random.randint(0, 4) 
                        if attack in [0, 4]: 
                            self.soul_heads.append([self.bx, self.by - 40, random.uniform(-4,4), -6, 150])
                            self.soul_heads.append([self.bx, self.by - 40, random.uniform(-4,4), -6, 150])
                        elif attack == 1: 
                            angle = math.atan2(self.py + 32 - (self.by - 40), self.px + 24 - self.bx)
                            self.boss_bullets.append([self.bx, self.by - 40, math.cos(angle)*14, math.sin(angle)*14, "witch_fireball"])
                        elif attack == 2: 
                            angle = math.atan2(self.py + 32 - (self.by - 40), self.px + 24 - self.bx)
                            self.boss_bullets.append([self.bx, self.by - 40, math.cos(angle)*18, math.sin(angle)*18, "witch_freeze"])
                        elif attack == 3: 
                            self.geyser_warns.append([self.px + 24, 40])
                
                elif self.boss_action == 1: 
                    if self.skeletons_to_spawn > 0 and pyxel.frame_count % 25 == 0: 
                        sx = random.choice([-40, SCREEN_WIDTH + 40])
                        self.skeletons.append([sx, FLOOR_Y, 10, 1 if sx < 0 else -1])
                        self.skeletons_to_spawn -= 1
                        if random.random() < 0.3: self.play_sfx(61) 

                    if self.skeletons_to_spawn == 0 and len(self.skeletons) == 0:
                        self.boss_action = 2
                        self.bx, self.by = 256, FLOOR_Y - 80
                        self.boss_immune = False; self.witch_darkness = False
                        self.play_music() 
                        self.spawn_explosion(self.bx, self.by - 40, 100, 13, 0)
                        self.trigger_impact(30, flash=True); self.play_sfx(56)

                elif self.boss_action == 2: 
                    self.boss_timer += 1
                    self.bx += math.sin(pyxel.frame_count * 0.05) * 2.0
                    self.by = FLOOR_Y - 80 + math.sin(pyxel.frame_count * 0.1) * 10
                    
                    if self.boss_timer > 25: 
                        self.boss_timer = 0
                        attack = random.randint(0, 3)
                        if attack == 1 and getattr(self, 'witch_quakes', 0) >= 3:
                            attack = random.choice([0, 2, 3]) 
                            
                        if attack == 0: 
                            angle = math.atan2(self.py + 32 - (self.by - 40), self.px + 24 - self.bx)
                            self.boss_bullets.append([self.bx, self.by - 40, math.cos(angle)*10.0, math.sin(angle)*10.0, "poison"])
                        elif attack == 1: 
                            self.shake = 40; self.witch_quakes = getattr(self, 'witch_quakes', 0) + 1
                            for _ in range(5):
                                self.earth_spikes.append([random.randint(40, 472), random.randint(20, 40), 0])
                        elif attack in [2, 3]: 
                            self.soul_heads.append([self.bx, self.by - 40, random.uniform(-4,4), -6, 150])
                            
                elif self.boss_action == 3: 
                    self.boss_vy += GRAVITY
                    self.by += self.boss_vy
                    if self.by > FLOOR_Y: self.by = FLOOR_Y; self.boss_vy = 0
                    
                    dist_x = (self.px + 24) - self.bx
                    self.b_dir = 1 if dist_x > 0 else -1
                    if abs(dist_x) > 20:
                        self.bx += 4.0 * self.b_dir
                    
                    if abs(dist_x) < 40 and abs((self.py + 32) - self.by) < 60:
                        if not self.immortal and pyxel.frame_count % 45 == 0:
                            dmg = int(self.max_hp * 0.20) 
                            self.p_hp -= dmg
                            self.trigger_impact(30); self.spawn_explosion(self.px + 24, self.py + 32, 60, 8, 2)

        self.p_hp = max(0, self.p_hp)
        self.boss_hp = max(0, self.boss_hp)

        if self.p_hp <= 0:
            self.state = 4
            return

        if self.boss_hp <= 0 and self.level > 0:
            if self.level == 7:
                self.game_beaten = True
            self.state = 5
            self.play_sfx(56)
            return

    # ==========================================
    # --- RENDER/DRAW LOGIC ---
    # ==========================================
    def draw_glow(self, x, y, radius, color_layers):
        step = radius / len(color_layers)
        for i, col in enumerate(color_layers): pyxel.circ(x, y, radius - (i * step), col)

    def draw_text_scaled(self, x, y, text, col, scale=2):
        text = str(text).upper()
        curr_x = x
        for char in text:
            if char in self.FONT:
                rows = self.FONT[char]
                for r, row_val in enumerate(rows):
                    for c in range(3):
                        if (row_val >> (2 - c)) & 1:
                            pyxel.rect(curr_x + c*scale, y + r*scale, scale, scale, col)
            curr_x += 4 * scale

    def text_shadow(self, x, y, text, col):
        self.draw_text_scaled(x+2, y+2, text, 0, scale=2)
        self.draw_text_scaled(x, y, text, col, scale=2)
        
    def text_thick(self, x, y, text, col):
        if text is None: return
        self.draw_text_scaled(x-2, y, text, 0, scale=2)
        self.draw_text_scaled(x+2, y, text, 0, scale=2)
        self.draw_text_scaled(x, y-2, text, 0, scale=2)
        self.draw_text_scaled(x, y+2, text, 0, scale=2)
        self.draw_text_scaled(x+2, y+2, text, 0, scale=2)
        self.draw_text_scaled(x, y, text, col, scale=2)

    def draw_scene_forest(self):
        t = pyxel.frame_count
        pyxel.cls(12)
        
        # Sun
        pyxel.circ(256, 150, 60, 10); pyxel.circ(256, 150, 50, 9)
        
        # Parallax Mountains
        for i, col in enumerate([1, 5, 12]):
            shift = (t * (0.2 + i*0.2)) % 512
            pyxel.tri(256 - shift, 350, 100 - shift, 150 + i*40, -50 - shift, 350, col)
            pyxel.tri(512 - shift, 350, 350 - shift, 120 + i*40, 150 - shift, 350, col)
            pyxel.tri(768 - shift, 350, 600 - shift, 180 + i*40, 400 - shift, 350, col)
            # Wrap around
            pyxel.tri(256 - shift + 512, 350, 100 - shift + 512, 150 + i*40, -50 - shift + 512, 350, col)

        # Dynamic River
        pyxel.rect(0, 350, 512, 162, 3) 
        for y in range(350, 512, 2):
            wave = math.sin(t * 0.05 + y * 0.1) * 30
            river_w = 60 + (y - 350) * 2.0
            river_x = 256 + wave
            pyxel.rect(river_x - river_w, y, river_w * 2, 2, 6) 
            if (y + t//2) % 8 < 4:
                pyxel.line(river_x - river_w + 15, y, river_x + river_w - 15, y, 12) 
                
        # Forest Trees (Foreground Frames)
        pyxel.rect(20, 200, 60, 312, 4); pyxel.rect(50, 200, 15, 312, 0)
        pyxel.rect(420, 150, 70, 362, 4); pyxel.rect(430, 150, 20, 362, 0)
        
        # Foliage Clusters
        for i in range(8):
            cx = 50 + math.sin(i*2) * 40
            cy = 100 + i * 20
            pyxel.circ(cx, cy, 60, 11 if i%2==0 else 3)
            cx2 = 460 + math.cos(i*2) * 50
            pyxel.circ(cx2, cy, 70, 11 if i%2!=0 else 3)

        # Falling Leaves Particle System
        for i in range(25):
            lx = (i * 70 - t * 2) % 512
            ly = (100 + i * 50 + math.sin(t * 0.1 + i) * 30) % 512
            pyxel.rect(lx, ly, 6, 4, 9); pyxel.pset(lx-1, ly+2, 8)
            
        # Tiny Rabbit
        rx = 256 + wave * 0.5 - 60
        ry = 400
        pyxel.rect(rx, ry, 12, 8, 15); pyxel.line(rx-4, ry-8, rx+4, ry, 15)
        pyxel.line(rx-6, ry-12, rx-4, ry-8, 15); pyxel.line(rx-2, ry-14, rx, ry-8, 15)

    def draw_scene_village(self):
        # A flat, layered 2D Diorama. Everything faces forward, highly detailed.
        t = pyxel.frame_count
        pyxel.cls(12) # Bright blue sky
        
        # --- LAYER 1: Distant Sky & Clouds ---
        for i in range(4):
            cx = (i * 250 + t * 0.3) % 800 - 150
            cy = 60 + (i % 2) * 40
            pyxel.circ(cx, cy, 80, 7); pyxel.circ(cx+60, cy-20, 60, 7)
            pyxel.circ(cx-60, cy+10, 70, 7); pyxel.circ(cx, cy+20, 80, 13)
        
        # Background Tree line
        pyxel.rect(0, 240, 512, 100, 3)
        for i in range(0, 512, 40):
            pyxel.circ(i, 230 + (i%30), 50, 3)
            pyxel.circ(i+20, 240, 40, 1)

        # --- LAYER 2: Background Lane & Small Houses ---
        pyxel.rect(0, 300, 512, 212, 11) # Base grass
        self.draw_cottage(40, 200, 100, 120, 8, 4)  # Left Back House
        self.draw_cottage(380, 220, 120, 100, 14, 2) # Right Back House

        # --- LAYER 3: Midground Main Path & Action ---
        pyxel.rect(0, 350, 512, 80, 4) # Flat dirt/cobble path lane
        for x in range(0, 512, 32):
            for y in range(350, 430, 16):
                if (x+y)%3 == 0: pyxel.rect(x + (y%20), y, 16, 8, 5) # Cobblestone texture
                
        # Main Central House (High detail)
        self.draw_cottage(180, 160, 160, 160, 9, 4)
        
        # Populating the path with moving flat micro-sprites
        k_x = (t * 1.5) % 800 - 100
        self.draw_knight(k_x, 400, 1)
        
        w_x = 600 - ((t * 0.8) % 800)
        self.draw_wizard(w_x, 380, -1)
        
        m_x = 250 + math.sin(t * 0.02) * 60
        self.draw_villager(m_x, 410, 1 if math.cos(t*0.02) > 0 else -1, maid=True)

        # --- LAYER 4: Foreground Elements ---
        pyxel.rect(0, 430, 512, 82, 3) # Foreground dark grass
        for i in range(0, 512, 16): pyxel.line(i, 430, i-4, 420, 3) # Grass blades
        
        # Livestock Pen (Foreground Left)
        pyxel.rect(20, 440, 140, 60, 2) # Mud
        pyxel.rectb(16, 436, 148, 68, 0); pyxel.rectb(18, 438, 144, 64, 4) # Fence
        pyxel.line(16, 456, 164, 456, 4); pyxel.line(16, 476, 164, 476, 4) # Horizontal fence posts
        self.draw_livestock(60 + math.sin(t*0.03)*20, 480)
        self.draw_livestock(120 + math.cos(t*0.02)*15, 460)

    # --- FLAT DIORAMA HELPER FUNCTIONS ---
    def draw_cottage(self, cx, cy, w, h, roof_color, wood_color):
        t = pyxel.frame_count
        # Main Wooden Structure
        pyxel.rect(cx, cy, w, h, wood_color)
        # Detailed Wood Planks
        for i in range(0, h, 8):
            pyxel.line(cx, cy+i, cx+w, cy+i, 0)
            if i+2 < h: pyxel.line(cx, cy+i+2, cx+w, cy+i+2, 2 if wood_color == 4 else 0)

        # Stone Foundation
        pyxel.rect(cx-4, cy+h-16, w+8, 16, 13)
        for i in range(0, w, 12):
            pyxel.circ(cx+i+4, cy+h-8, 4, 5); pyxel.circ(cx+i+10, cy+h-4, 3, 1)

        # Roof (Layered horizontal bands simulating shingles/thatch)
        roof_h = h // 1.2
        pyxel.tri(cx-20, cy, cx+w//2, cy-roof_h, cx+w+20, cy, 0) # Roof backdrop/shadow
        for i in range(int(roof_h)):
            width = (w + 40) * (1 - (i / roof_h))
            rx = cx + w//2 - width//2
            ry = cy - i
            # Create a textured band effect
            r_col = roof_color if i % 6 < 3 else (roof_color - 1 if roof_color > 0 else 0)
            pyxel.line(rx, ry, rx + width, ry, r_col)

        # Door
        dx, dy = cx + 20, cy + h - 46
        pyxel.rect(dx, dy, 28, 30, 0); pyxel.rect(dx+2, dy+2, 24, 28, 2)
        pyxel.circ(dx+20, dy+15, 2, 10) # Glowing doorknob

        # Window
        wx, wy = cx + w - 46, cy + h//2 - 20
        pyxel.rect(wx-4, wy-4, 32, 32, 0) # Frame
        glow = 10 if (t//20)%2 == 0 else 9
        pyxel.rect(wx, wy, 24, 24, glow)
        pyxel.line(wx+12, wy, wx+12, wy+24, 0); pyxel.line(wx, wy+12, wx+24, wy+12, 0) # Mullions
        # Silhouette inside
        if (t//40)%2 == 0: pyxel.rect(wx+4, wy+8, 8, 16, 0)

        # Hanging Lantern
        lx, ly = cx - 10, cy + 10
        pyxel.line(cx, cy+5, lx+4, ly, 0)
        pyxel.rect(lx, ly, 8, 12, 0); pyxel.rect(lx+2, ly+2, 4, 8, 10)
        self.draw_glow(lx+4, ly+6, 16, [10, 9, 8])

    def draw_knight(self, x, y, facing):
        # Tall, detailed armor
        pyxel.rect(x-8, y-36, 16, 24, 13) # Chestplate
        pyxel.rect(x-6, y-12, 6, 12, 5); pyxel.rect(x+2, y-12, 6, 12, 5) # Legs
        # Helmet
        pyxel.rect(x-7, y-48, 14, 12, 6)
        pyxel.rect(x-8, y-44, 16, 6, 13) # Visor
        pyxel.rect(x + (2 if facing==1 else -6), y-42, 4, 2, 0) # Eye slit
        # Sword
        sx = x + (12 if facing==1 else -12)
        pyxel.rect(sx-2, y-30, 4, 30, 7) # Blade
        pyxel.rect(sx-6, y-20, 12, 4, 9) # Guard

    def draw_wizard(self, x, y, facing):
        # Flowing Robe
        pyxel.tri(x-12, y, x+12, y, x, y-36, 2)
        pyxel.tri(x-8, y, x+8, y, x, y-36, 1)
        # Giant Hat
        pyxel.tri(x-16, y-30, x+16, y-30, x, y-56, 12)
        pyxel.line(x-20, y-30, x+20, y-30, 12)
        # Staff
        sx = x + (14 if facing==1 else -14)
        pyxel.rect(sx-2, y-44, 4, 44, 4)
        pyxel.circ(sx, y-48, 5, 11) # Gem
        if pyxel.frame_count % 10 < 5: pyxel.circ(sx, y-48, 2, 7)

    def draw_villager(self, x, y, facing, maid=False):
        color = 14 if maid else 8
        skin = 15
        pyxel.rect(x-6, y-24, 12, 24, color) # Body
        if maid: pyxel.rect(x-4, y-16, 8, 16, 7) # Apron
        pyxel.rect(x-5, y-34, 10, 10, skin) # Head
        pyxel.rect(x-6, y-36, 12, 4, 4) # Hair/Cap
        pyxel.pset(x + (2 if facing==1 else -2), y-30, 0) # Eye

    def draw_livestock(self, x, y):
        pyxel.rect(x-10, y-12, 20, 12, 14) # Pig body
        pyxel.rect(x-8, y-4, 4, 4, 14); pyxel.rect(x+4, y-4, 4, 4, 14) # Legs
        hx = x + (10 if pyxel.frame_count%40<20 else -14)
        pyxel.rect(hx, y-10, 8, 8, 14) # Head
        pyxel.pset(hx + (4 if pyxel.frame_count%40<20 else 2), y-8, 0) # Eye

    def draw_scene_swamp(self):
        t = pyxel.frame_count
        pyxel.cls(1) # Dark midnight blue
        
        # Full Moon
        pyxel.circ(256, 150, 140, 5)
        
        # Murky Water
        pyxel.rect(0, 350, 512, 162, 0)
        for y in range(350, 512, 8):
            pyxel.line(0, y, 512, y, 1)
            pyxel.line(0, y+2, 512, y+2, 5 if (y+t)%16 < 8 else 1)
            
        # Massive Ancient Tree (Colossus)
        pyxel.circ(256, 100, 200, 0) # Dark canopy back
        pyxel.circ(200, 150, 180, 1) # Mid canopy
        pyxel.circ(230, 180, 150, 2) # Front canopy
        
        # Sprawling roots & trunk (Dithered shadow style)
        pyxel.tri(50, 100, 180, 100, 20, 450, 0)
        pyxel.tri(100, 100, 180, 100, 80, 450, 1)
        pyxel.tri(330, 100, 450, 100, 480, 450, 0)
        pyxel.tri(330, 100, 400, 100, 430, 450, 1)
        pyxel.tri(200, 100, 312, 100, 256, 480, 0)
        pyxel.tri(230, 100, 280, 100, 256, 480, 2)
        
        # Hanging Moss
        for i in range(16):
            mx = 40 + i * 30
            my = 120 + math.sin(i) * 80
            sway = math.sin(t * 0.05 + i) * 20
            pyxel.line(mx, my, mx + sway, my + 150 + i*15, 3)
            pyxel.line(mx+4, my, mx + 4 + sway, my + 180 + i*15, 11)

        # Scrolling Fog
        for y in range(300, 512, 20):
            fx = math.sin(t * 0.02 + y) * 120
            pyxel.rect(int(fx), y, 512, 12, 13 if y % 40 == 0 else 5)
            # Fog dithering
            for x in range(0, 512, 8):
                pyxel.pset(int(x + fx), y + 6, 1)
                
        # Little Boat
        bx = 350
        by = 420 + math.sin(t * 0.08) * 8
        pyxel.tri(bx, by, bx+120, by, bx+60, by+30, 4)
        pyxel.rect(bx+30, by, 60, 20, 0)

    def draw_scene_castle(self):
        t = pyxel.frame_count
        pyxel.cls(12)
        
        # Verdant Hills Base
        pyxel.circ(100, 550, 350, 3)
        pyxel.circ(450, 600, 400, 11)
        pyxel.circ(256, 650, 300, 3)
        
        # Grass dithering
        for i in range(400):
            gx = (i * 37) % 512
            gy = 350 + (i * 13) % 162
            pyxel.line(gx, gy, gx+15, gy, 1)
            
        # Citadel
        cx, cy = 250, 80
        pyxel.rect(cx, cy, 180, 250, 0)
        pyxel.rect(cx+10, cy+10, 160, 240, 5) # Stone Walls
        pyxel.rect(cx+20, cy+10, 100, 240, 13) # Highlight
        
        # Left Spire
        pyxel.rect(cx-30, cy-60, 50, 310, 5)
        pyxel.rect(cx-15, cy-60, 25, 310, 13)
        pyxel.tri(cx-40, cy-60, cx+40, cy-60, cx-5, cy-150, 8) # Red Roof
        pyxel.tri(cx-30, cy-60, cx+30, cy-60, cx-5, cy-140, 14) # Roof highlight
        
        # Right Spire
        pyxel.rect(cx+160, cy-40, 50, 290, 5)
        pyxel.rect(cx+175, cy-40, 25, 290, 13)
        pyxel.tri(cx+150, cy-40, cx+230, cy-40, cx+185, cy-130, 8)
        pyxel.tri(cx+160, cy-40, cx+220, cy-40, cx+185, cy-120, 14)
        
        # Huge Gate
        pyxel.circ(cx+90, cy+200, 50, 0)
        pyxel.rect(cx+40, cy+200, 100, 50, 0)
        # Giant Rose Window
        pyxel.circ(cx+90, cy+90, 30, 10)
        pyxel.rect(cx+60, cy+90, 60, 40, 10)
        pyxel.line(cx+90, cy+60, cx+90, cy+130, 0)
        pyxel.line(cx+60, cy+105, cx+120, cy+105, 0)

        # Cloud Shadows moving across hills
        sx = (t * 3) % 1024 - 256
        pyxel.circb(sx, 450, 120, 1)
        for r in range(60, 120, 6):
            pyxel.circb(sx, 450, r, 2)
        sx2 = (t * 2.5 + 300) % 1024 - 256
        for r in range(40, 90, 6):
            pyxel.circb(sx2, 500, r, 2)

    def draw_scene_evil(self):
        t = pyxel.frame_count
        pyxel.cls(1) # Dark void
        
        # Glowing Blood Moon
        self.draw_glow(256, 200, 180, [1, 5, 12]) 
        pyxel.circ(256, 200, 120, 10) 
        
        # Dead Terrain
        pyxel.rect(0, 400, 512, 112, 0)
        pyxel.tri(50, 512, 150, 300, 256, 512, 0)
        pyxel.tri(200, 512, 350, 350, 512, 512, 0)
        
        # The Witch (Imposing silhouette)
        float_y = math.sin(t * 0.05) * 20
        pyxel.tri(100, 512, 256, 150 + float_y, 412, 512, 0)
        pyxel.tri(50, 512, 256, 200 + float_y, 462, 512, 1) # Cloak folds
        pyxel.tri(150, 512, 256, 150 + float_y, 362, 512, 0) 
        
        # Head & Glowing Eyes
        pyxel.circ(256, 150 + float_y, 40, 0)
        self.draw_glow(240, 140 + float_y, 12, [2, 8])
        self.draw_glow(272, 140 + float_y, 12, [2, 8])
        
        # Swirling Void Magic
        for i in range(100):
            mx = (t * 2 + i * 25) % 600 - 50
            my = 400 + math.sin(t * 0.02 + i) * 100
            msize = 10 + math.sin(i) * 20
            pyxel.circ(mx, my, msize, [0, 1, 2][i % 3])
            
        # Scary Ravens
        for i in range(20):
            rx = 256 + math.cos(t * 0.05 + i) * (100 + i * 10)
            ry = 150 + float_y + math.sin(t * 0.05 + i) * (100 + i * 10)
            pyxel.rect(rx, ry, 6, 2, 0); pyxel.tri(rx+2, ry, rx-4, ry-4, rx+4, ry-4, 0)

    def draw_story(self):
        t = self.story_timer
        
        # Direct the cinematic flow
        if t < 200:
            self.draw_scene_forest()
            txt1, txt2 = "THE MYSTICAL WORLD OF LAGOONICA...", ""
        elif t < 400:
            self.draw_scene_village()
            txt1, txt2 = "A ONCE PROSPERING AND BOUNTIFUL", "REALM OF MAGIC AND BEAUTY."
        elif t < 600:
            self.draw_scene_swamp()
            txt1, txt2 = "WHERE LIFE FLOURISHED IN EVERY CORNER.", ""
        elif t < 800:
            self.draw_scene_castle()
            txt1, txt2 = "AND KINGDOMS STOOD PROUD.", ""
        else:
            self.draw_scene_evil()
            if t > 850:
                txt1, txt2 = "BUT IT WAS SOON ENGULFED BY", "THE WRATH OF THE MALEVOLENT WITCH."
            else:
                txt1, txt2 = "", ""
            if t > 1050:
                txt1, txt2 = "YOU, WARLOCK, MUST ERADICATE HER TYRANNY.", "BRING PEACE TO THESE LANDS ONCE MORE."
        
        # Highly legible text boxes for the intro
        if txt1:
            pyxel.rect(20, 30, len(txt1) * 16 + 20, 30, 0)
            self.text_shadow(30, 40, txt1, 7)
        if txt2:
            pyxel.rect(20, 70, len(txt2) * 16 + 20, 30, 0)
            self.text_shadow(30, 80, txt2, 7 if t <= 850 else 8)
                
        pyxel.rect(5, 475, 320, 30, 0)
        self.text_shadow(15, 480, "PRESS SPACE TO SKIP", 5)

    def draw_peaceful_background(self):
        pyxel.cls(9) 
        for y in range(320):
            if y < 120: col = 14 
            elif y < 220: col = 8 
            else: col = 9 
            pyxel.line(0, y, 512, y, col)
            
        pyxel.circ(256, 240, 50, 10); pyxel.circ(256, 240, 40, 9); pyxel.circ(256, 240, 30, 7)
        
        pyxel.tri(-60, 360, 120, 120, 280, 360, 5) 
        pyxel.tri(180, 360, 360, 80, 540, 360, 1) 
        pyxel.tri(400, 360, 480, 180, 580, 360, 5) 
        pyxel.pset(120, 122, 7); pyxel.pset(360, 82, 7)
        
        pyxel.rect(0, 340, 512, 172, 14) 
        for y in range(348, SCREEN_HEIGHT, 8):
            if y % 16 == 0: pyxel.rect(0, y, 512, 8, 3) 
            else: pyxel.rect(0, y, 512, 8, 14)
            
        for f in getattr(self, 'flowers', []):
            sway = math.sin(pyxel.frame_count * 0.04 + f["y"] * 0.1 + f["sway_offset"]) * 3.0
            fx = f["x"] + sway; fy = f["y"]; col = f["color"]
            
            pyxel.line(fx, fy, fx - sway*0.5, fy+16, 3) 
            if f["size"] == 1:
                pyxel.pset(fx, fy, col); pyxel.pset(fx, fy-2, 7) 
            else:
                pyxel.circ(fx, fy, 2, col); pyxel.pset(fx, fy, 7) 
        
        for b in getattr(self, 'butterflies', []):
            bx, by = int(b["x"]), int(b["y"]); col = b["color"]
            if b["flap_timer"] % 20 < 10:
                pyxel.line(bx, by-6, bx, by+6, 0); pyxel.rect(bx-2, by-4, 6, 10, col)
            else:
                pyxel.tri(bx-8, by-4, bx, by, bx-4, by+6, col); pyxel.tri(bx+8, by-4, bx, by, bx+4, by+6, col)
        
        for bird in getattr(self, 'bird_flock', []):
            bx, by_final = int(bird["x"]), int(bird["y"])
            wing_sway = math.sin(pyxel.frame_count * 0.2 + bird["w_offset"])
            pyxel.rect(bx, by_final, 8, 4, 0) 
            if wing_sway > 0: pyxel.tri(bx+2, by_final, bx-6, by_final-8, bx+6, by_final-8, 0)
            else: pyxel.tri(bx+2, by_final+4, bx-6, by_final+12, bx+6, by_final+12, 0)
            
        for leaf in getattr(self, 'autumn_leaves', []):
            lx, ly = int(leaf[0]), int(leaf[1]); l_type = leaf[3]; c1, c2 = leaf[4], leaf[5]
            if l_type == 0: pyxel.line(lx, ly, lx+6, ly+2, c1); pyxel.pset(lx+2, ly+2, c2)
            elif l_type == 1: pyxel.rect(lx, ly, 4, 4, c2); pyxel.pset(lx+4, ly, c1); pyxel.pset(lx-2, ly+2, c1)
            else: pyxel.rect(lx, ly, 6, 4, c1); pyxel.pset(lx-2, ly, c2); pyxel.pset(lx+6, ly+2, c2)
        
        for ff in getattr(self, 'fireflies', []):
            blink = ff[2] % 60
            intensity = blink / 30.0 if blink < 30 else (60 - blink) / 30.0 
            if intensity > 0.2:
                self.draw_glow(int(ff[0]), int(ff[1]), 4, [0, 5, 11]); pyxel.pset(int(ff[0]), int(ff[1]), 7) 
        
        tx, ty = 60, 200
        pyxel.rect(tx, ty+10, 30, 140, 0); pyxel.rect(tx+6, ty+16, 18, 128, 4) 
        pyxel.circ(tx+14, ty, 70, 0); pyxel.circ(tx+14, ty, 64, 3) 
        pyxel.circ(tx-20, ty+40, 50, 0); pyxel.circ(tx-20, ty+40, 44, 3) 
        pyxel.circ(tx+50, ty+30, 40, 0); pyxel.circ(tx+50, ty+30, 34, 3) 
        for _ in range(100):
            pyxel.pset(tx + random.randint(-60, 80), ty + random.randint(-60, 80), random.choice([3, 11]))
        
        for drip in getattr(self, 'mage_blood_drips', []):
            if len(drip) >= 2: pyxel.circ(int(drip[0]), int(drip[1]), 2, 8)
            
        self.px, self.py = 256 - 24, 340 - 64
        self.draw_detailed_mage()
        
        if getattr(self, 'dove', None):
            dx, dy = int(self.dove["x"]), int(self.dove["y"])
            if self.dove["state"] == "flying":
                flap = math.sin(pyxel.frame_count * 0.5)
                pyxel.rect(dx, dy, 12, 6, 7); pyxel.pset(dx+10, dy-2, 7); pyxel.pset(dx+12, dy-2, 8) 
                if flap > 0: pyxel.tri(dx+4, dy, dx-4, dy-10, dx+8, dy-12, 7)
                else: pyxel.tri(dx+4, dy+4, dx-4, dy+14, dx+8, dy+12, 7) 
            else:
                pyxel.rect(dx-4, dy-4, 12, 8, 7); pyxel.rect(dx+4, dy-8, 6, 6, 7) 
                pyxel.pset(dx+10, dy-6, 8); pyxel.line(dx, dy, dx-8, dy+6, 7) 
        
        for p in self.particles: p.draw()

    def draw_epic_background(self):
        t = pyxel.frame_count
        pyxel.cls(0)
        
        if getattr(self, 'level', 1) == 6:
            bg_col = 2 if (self.boss_hp / self.boss_max_hp) <= 0.8 else 1
            for y in range(160):
                if y % 4 == 0: pyxel.line(0, y*2, 512, y*2, bg_col)
            pyxel.rect(0, 160, 512, 160, bg_col)
        elif getattr(self, 'level', 1) == 7:
            for y in range(160):
                if y % 5 == 0: pyxel.line(0, y*2, 512, y*2, 13)
            pyxel.rect(0, 160, 512, 160, 0)
        else:
            for y in range(160):
                if y > 120: pyxel.line(0, y*2, 512, y*2, 1)
                elif y > 80 and y % 2 == 0: pyxel.line(0, y*2, 512, y*2, 1)
                elif y > 40 and y % 3 == 0: pyxel.line(0, y*2, 512, y*2, 1)
                elif y % 4 == 0: pyxel.line(0, y*2, 512, y*2, 1)
            pyxel.rect(0, 160, 512, 160, 1)

        if getattr(self, 'level', 1) == 5 and getattr(self, 'boss_action', 0) == 1:
            cx, cy = 256, 240
            pyxel.circ(cx, cy, 140, 2) 
            pyxel.circ(cx, cy-100, 80, 0) 
            leg_t = t * 0.02
            for i in range(8):
                ang = (i/8.0) * math.pi * 2 + leg_t
                dist = 320 
                ex = cx + math.cos(ang) * dist; ey = cy + math.sin(ang) * dist
                mid_x = cx + math.cos(ang) * (dist*0.6); mid_y = cy + math.sin(ang) * (dist*0.3)
                pyxel.line(cx, cy, mid_x, mid_y, 0); pyxel.line(mid_x, mid_y, ex, ey, 2) 

        mx, my = 80, 80
        if getattr(self, 'level', 1) == 6:
            pyxel.circ(mx, my, 32, 8); pyxel.circ(mx - 8, my - 8, 28, 2); pyxel.circb(mx, my, 36, 2)
        elif getattr(self, 'level', 1) == 7:
            pyxel.circ(mx, my, 32, 13); pyxel.circ(mx - 4, my - 4, 28, 0); pyxel.circb(mx, my, 36, 5) 
        else:
            pyxel.circ(mx, my, 32, 10); pyxel.circ(mx - 8, my - 8, 28, 0); pyxel.circb(mx, my, 36, 9) 

        for i in range(4):
            cx = ((t * (0.4 + i*0.2)) % 700) - 100; cy = 30 + i * 40
            pyxel.rect(cx, cy, 100 + i*30, 6 + i%4, 5); pyxel.rect(cx + 20, cy - 4, 60 + i*20, 4, 1)

        bx, by = int(self.bird["x"]), int(self.bird["y"])
        pyxel.line(bx, by, bx-8, by-6, 0); pyxel.line(bx, by, bx+8, by-6, 0)
        if t % 10 < 5: pyxel.line(bx, by, bx-8, by+4, 0); pyxel.line(bx, by, bx+8, by+4, 0)

        for i in range(5):
            px = (i * 130 - (t * 0.1) % 130) - 80
            pyxel.tri(px, 320, px+80, 140+i*20, px+160, 320, 2 if self.level != 7 else 1) 
            pyxel.tri(px+80, 140+i*20, px+110, 200+i*20, px+160, 320, 3 if self.level != 7 else 5) 

        cast_x, cast_y = 300, 260
        if getattr(self, 'level', 1) != 7:
            pyxel.rect(cast_x, cast_y-100, 80, 160, 0); pyxel.tri(cast_x-20, cast_y+60, cast_x+40, cast_y, cast_x+100, cast_y+60, 0)
            pyxel.rect(cast_x-10, cast_y-140, 24, 100, 0); pyxel.tri(cast_x-14, cast_y-140, cast_x+2, cast_y-190, cast_x+18, cast_y-140, 0)
            pyxel.rect(cast_x+66, cast_y-120, 24, 80, 0); pyxel.tri(cast_x+62, cast_y-120, cast_x+78, cast_y-170, cast_x+94, cast_y-120, 0)
            pyxel.rect(cast_x+4, cast_y-80, 12, 120, 5); pyxel.rect(cast_x+70, cast_y-60, 12, 100, 5)
            glow = 8 if getattr(self, 'level', 1) == 6 else (9 if (t % 30 < 15) else 10)
            pyxel.rect(cast_x+24, cast_y-70, 32, 40, glow); pyxel.circ(cast_x+40, cast_y-70, 16, glow)
            pyxel.line(cast_x+40, cast_y-86, cast_x+40, cast_y-30, 0); pyxel.line(cast_x+24, cast_y-50, cast_x+56, cast_y-50, 0)

        if self.whale["active"]:
            wx, wy = int(self.whale["x"]), int(self.whale["y"])
            pyxel.circ(wx, wy, 16, 0); pyxel.circ(wx - 8, wy + 4, 12, 1) 
            tail_angle = math.atan2(self.whale["vy"], self.whale["vx"]) + 3.14
            tx = wx + math.cos(tail_angle) * 20; ty = wy + math.sin(tail_angle) * 20
            pyxel.line(wx, wy, tx, ty, 0); pyxel.tri(tx, ty, tx-10, ty-10, tx-4, ty+12, 0)

        for y in range(320, 400):
            wave_offset = math.sin(t * 0.05 + y * 0.1) * (y - 300) * 0.05 
            pyxel.line(0, y, 512, y, 1) 
            reflect_width = (y - 300) * 0.3
            if (y + t//4) % 3 != 0: 
                pyxel.line(80 - reflect_width + wave_offset, y, 80 + reflect_width + wave_offset, y, 5 if y%3==0 else 1)

        for s in self.water_splashes: pyxel.circb(s[0], s[1], s[2], 7 if s[2] < 4 else 5)
        for m in self.mist: pyxel.line(m[0], m[1], m[0] + 30, m[1], 5 if m[1]%2==0 else 1)
        
        # --- SOLID TEXTURED FLOOR ---
        pyxel.rect(0, 400, 512, 112, 3) 
        for dy in range(400, 512, 8):
            pyxel.line(0, dy, 512, dy, 0) 
            for dx in range((dy % 16), 512, 16):
                pyxel.line(dx, dy, dx, dy + 8, 0)
        
        for i in range(32):
            px = (i * 36 - (t * 0.5) % 36) - 20
            tree_height = 360 + (i % 3) * 30
            pyxel.line(px, 512, px, tree_height, 0) 
            for ty in range(tree_height, 512, 6):
                w = (ty - tree_height) * 0.4
                pyxel.line(px - w, ty, px + w, ty, 3); pyxel.line(px - w + 2, ty + 2, px + w - 2, ty + 2, 0) 
                
        for l in getattr(self, 'leaves', []):
            lx, ly = int(l[0]), int(l[1])
            l_type = l[2] if len(l) > 2 else int(lx) % 3
            c1, c2 = 11, 3 
            if l_type == 0:
                pyxel.line(lx, ly, lx+6, ly+2, c1); pyxel.pset(lx+2, ly+2, c2)
            elif l_type == 1:
                pyxel.rect(lx, ly, 4, 4, c2); pyxel.pset(lx+4, ly, c1); pyxel.pset(lx-2, ly+2, c1)
            else:
                pyxel.rect(lx, ly, 6, 4, c1); pyxel.pset(lx-2, ly, c2); pyxel.pset(lx+6, ly+2, c2)
            
        for r in self.rain: 
            rain_col = 5 if r[1] % 2 == 0 else 1
            if getattr(self, 'level', 1) == 6 and r[1] % 2 == 0: rain_col = 8
            if getattr(self, 'level', 1) == 7: rain_col = 13
            pyxel.line(r[0], r[1], r[0], r[1]+4, rain_col)

    def draw_detailed_mage(self):
        robe_core = 12 if not self.p_poisoned else 11
        robe_trim = 4 if not self.p_poisoned else 3
        
        pyxel.rect(self.px + 4, self.py + 60, 40, 6, 0) 
        pyxel.rect(self.px - 4, self.py + 36, 12, 16, robe_trim)
        pyxel.rect(self.px - 2, self.py + 38, 8, 12, 7) 
        pyxel.line(self.px - 2, self.py + 44, self.px + 4, self.py + 44, 8) 
        pyxel.rect(self.px + 8, self.py + 24, 32, 36, robe_core)
        pyxel.rect(self.px + 16, self.py + 24, 16, 36, 1) 
        pyxel.line(self.px + 12, self.py + 28, self.px + 12, self.py + 56, 13) 
        pyxel.line(self.px + 36, self.py + 28, self.px + 36, self.py + 56, 13) 
        pyxel.rect(self.px + 8, self.py + 44, 32, 6, robe_trim)
        pyxel.rect(self.px + 20, self.py + 42, 12, 10, 10) 
        pyxel.rect(self.px + 22, self.py + 44, 8, 6, 0) 
        pyxel.tri(self.px + 8, self.py + 24, self.px + 40, self.py + 24, self.px + 24, self.py + 52, 7)
        pyxel.line(self.px + 16, self.py + 24, self.px + 24, self.py + 48, 6) 
        pyxel.line(self.px + 32, self.py + 24, self.px + 24, self.py + 48, 6)
        pyxel.rect(self.px + 16, self.py + 12, 16, 12, 15)
        pyxel.pset(self.px + 20, self.py + 16, 0); pyxel.pset(self.px + 28, self.py + 16, 0) 
        pyxel.line(self.px + 18, self.py + 12, self.px + 30, self.py + 12, robe_trim) 
        pyxel.tri(self.px + 4, self.py + 12, self.px + 44, self.py + 12, self.px + 24, self.py - 24, robe_core)
        pyxel.tri(self.px + 12, self.py + 8, self.px + 36, self.py + 8, self.px + 24, self.py - 16, 1) 
        pyxel.rect(self.px, self.py + 12, 48, 6, robe_core) 
        pyxel.line(self.px, self.py + 16, self.px + 48, self.py + 16, 1) 
        
        dx = pyxel.mouse_x - (self.px + 24); dy = pyxel.mouse_y - (self.py + 32)
        dist = math.hypot(dx, dy)
        if dist != 0:
            sx = self.px + 24 + (dx / dist) * 32; sy = self.py + 32 + (dy / dist) * 32
            pyxel.line(self.px + 22, self.py + 32, sx-2, sy, 4)
            pyxel.line(self.px + 24, self.py + 32, sx, sy, 9) 
            pyxel.line(self.px + 26, self.py + 32, sx+2, sy, 4)
            self.draw_glow(sx, sy, 16, [1, 12, 11, 7]) 
            pyxel.pset(sx, sy, 7) 
            
        for i in range(getattr(self, 'latched_spiders', 0)):
            ox = self.px + random.randint(0, 40); oy = self.py + random.randint(0, 60)
            pyxel.circ(ox, oy, 2, 0); pyxel.pset(ox, oy, 8)

    def draw_detailed_boss(self):
        if getattr(self, 'boss_action', 0) == 1 and getattr(self, 'level', 1) == 7: return 
            
        hp_pct = self.boss_hp / self.boss_max_hp
        rage = hp_pct < 0.5 if self.level != 4 else hp_pct < 0.4
        t = pyxel.frame_count
        dist_x = (self.px + 24) - self.bx 
        is_frozen = getattr(self, 'boss_frozen_timer', 0) > 0
        
        bw, bh = 80, 100
        if self.level == 1: bw, bh = 60, 60
        elif self.level in [2, 3]: bw, bh = 80, 120
        elif self.level == 4: bw, bh = 80, 80
        elif self.level == 5: bw, bh = 80, 60
        elif self.level == 6: bw, bh = 60, 90
        elif self.level == 7: bw, bh = 50, 110
        elif self.level == 0: bw, bh = 40, 80

        if is_frozen: self.draw_glow(self.bx, self.by - bh//2, max(bw, bh), [1, 12, 7])
        elif getattr(self, 'boss_on_fire', 0) > 0: self.text_shadow(self.bx-20, self.by-bh-40, "ABLAZE", 8)
        
        if getattr(self, 'boss_freeze_stacks', 0) > 0 and not is_frozen:
            self.text_shadow(self.bx-20, self.by-bh-20, f"FROST {self.boss_freeze_stacks}/3", 12)
        
        if self.level == 0:
            pyxel.rect(self.bx - 16, self.by - 60, 32, 60, 4) 
            pyxel.rect(self.bx - 20, self.by - 80, 40, 20, 9) 
            pyxel.circ(self.bx, self.by - 50, 16, 15) 
            pyxel.circ(self.bx - 6, self.by - 50, 3, 0); pyxel.circ(self.bx + 6, self.by - 50, 3, 0)
        elif self.level == 1:
            bob = math.sin(pyxel.frame_count * 0.1) * 8
            by_off = self.by + bob
            pyxel.rect(self.bx - 12, by_off + 30, 24, 8, 0); pyxel.rect(self.bx - 24, by_off - 24, 48, 48, 0) 
            pyxel.rect(self.bx - 16, by_off - 16, 32, 32, 1)
            for i in range(4): pyxel.line(self.bx - 24 + i*12, by_off + 24, self.bx - 20 + i*12, by_off + 40, 0) 
            pyxel.rect(self.bx - 32, by_off - 16, 8, 16, 13); pyxel.rect(self.bx + 24, by_off - 16, 8, 16, 13)
            pyxel.rect(self.bx - 12, by_off - 12, 24, 16, 0) 
            self.draw_glow(self.bx - 6, by_off - 8, 8, [0, 2, 8]); self.draw_glow(self.bx + 6, by_off - 8, 8, [0, 2, 8]) 
            
        elif self.level in [2, 3]:
            arm_col = 5 if not rage else 1 
            vis_col = 8 
            by_off = self.by - 20
            walk_cycle = math.sin(pyxel.frame_count * 0.2) * 8 if getattr(self, 'boss_action', 0) in [0, 1] else 0
            
            pyxel.rect(self.bx - 20, by_off + 20, 40, 8, 0) 
            pyxel.rect(self.bx - 28, by_off - 88, 56, 80, 8); pyxel.rect(self.bx - 24, by_off - 88, 48, 80, 2) 
            pyxel.line(self.bx - 28, by_off - 8, self.bx - 20, by_off + 16, 8); pyxel.line(self.bx + 28, by_off - 8, self.bx + 20, by_off + 16, 8)
            pyxel.rect(self.bx - 20, by_off - 92, 40, 72, arm_col); pyxel.rect(self.bx - 12, by_off - 92, 24, 72, 1) 
            pyxel.line(self.bx - 20, by_off - 56, self.bx + 20, by_off - 56, 0); pyxel.line(self.bx - 20, by_off - 32, self.bx + 20, by_off - 32, 0)
            self.draw_glow(self.bx, by_off - 72, 12, [0, 2, 8, 9] if rage else [0, 1, 12, 11])
            pyxel.rect(self.bx - 16 - walk_cycle, by_off - 20, 12, 40, arm_col); pyxel.rect(self.bx + 4 + walk_cycle, by_off - 20, 12, 40, arm_col) 
            pyxel.rect(self.bx - 16, by_off - 112, 32, 28, arm_col) 
            pyxel.rect(self.bx - 24, by_off - 120, 8, 20, arm_col); pyxel.pset(self.bx - 24, by_off - 124, 6) 
            pyxel.rect(self.bx + 16, by_off - 120, 8, 20, arm_col); pyxel.pset(self.bx + 22, by_off - 124, 6) 
            pyxel.rect(self.bx - 8, by_off - 100, 16, 6, vis_col) 
            if rage: self.draw_glow(self.bx, by_off - 98, 16, [0, 2, 8])
            pyxel.rect(self.bx - 32, by_off - 92, 12, 24, arm_col); pyxel.rect(self.bx + 20, by_off - 92, 12, 24, arm_col) 

            if self.level == 2:
                sx, sy = self.bx + (24 * self.b_dir), by_off - 40
                ex = sx + math.cos(self.swing_angle) * 100; ey = sy + math.sin(self.swing_angle) * 100
                gx1 = sx + math.cos(self.swing_angle + 1.57) * 20; gy1 = sy + math.sin(self.swing_angle + 1.57) * 20
                gx2 = sx - math.cos(self.swing_angle + 1.57) * 20; gy2 = sy - math.sin(self.swing_angle + 1.57) * 20
                pyxel.line(gx1, gy1, gx2, gy2, 4); pyxel.circ(gx1, gy1, 2, 8); pyxel.circ(gx2, gy2, 2, 8) 
                pyxel.line(sx, sy, ex, ey, 7); pyxel.line(sx+2, sy+2, ex+2, ey+2, 10); pyxel.line(sx-2, sy-2, ex-2, ey-2, 8)
                pyxel.line(sx, sy, sx + math.cos(self.swing_angle)*60, sy + math.sin(self.swing_angle)*60, 0) 
                if rage: self.draw_glow(ex, ey, 24, [0, 8, 9, 10]) 
                
            elif self.level == 3:
                gx, gy = self.bx, by_off - 52
                gun_len = 48 if not rage else 60
                ex = gx + math.cos(self.boss_aim_angle) * gun_len; ey = gy + math.sin(self.boss_aim_angle) * gun_len
                pyxel.line(gx, gy, ex, ey, 0 if not rage else 2); pyxel.line(gx, gy+2, ex, ey+2, 5) 
                offset = pyxel.frame_count % 6
                pyxel.line(gx + math.cos(self.boss_aim_angle)*offset, gy + math.sin(self.boss_aim_angle)*offset, ex, ey, 6)
                pyxel.circ(ex, ey, 6 if not rage else 10, 13 if not rage else 7); pyxel.circ(ex, ey, 2, 0) 
                pyxel.line(gx, gy, self.bx, by_off - 20, 4)
                for i in range(0, 10, 3): pyxel.circ(gx + (self.bx-gx)*(i/10.0), gy + ((by_off-20)-gy)*(i/10.0), 2, 10)

        elif self.level == 4:
            by_off = self.by - 40
            fat_col = 8 if not (rage and self.lava_timer >= 330) else 2 
            pyxel.rect(self.bx - 40, by_off - 40, 80, 80, 0); pyxel.rect(self.bx - 36, by_off - 36, 72, 72, fat_col) 
            pyxel.rect(self.bx - 20, by_off - 20, 40, 40, 1 if fat_col == 8 else 8); pyxel.rect(self.bx - 24, by_off - 24, 48, 8, 0)
            if self.boss_action == 0: 
                t = pyxel.frame_count * 0.5 * self.b_dir
                for i in range(4):
                    sx = self.bx + math.cos(t + i*1.57) * 44; sy = by_off + math.sin(t + i*1.57) * 44
                    pyxel.circ(sx, sy, 6, 13)
            else: 
                pyxel.rect(self.bx - 52, by_off - 20, 16, 40, fat_col); pyxel.rect(self.bx + 36, by_off - 20, 16, 40, fat_col)
                
        elif self.level == 5:
            if getattr(self, 'boss_action', 0) != 1: 
                by_off = self.by - 30
                t = pyxel.frame_count * 0.3
                for i in range(4):
                    lx = self.bx + math.cos(t + i) * 40; ly = by_off + math.sin(t + i) * 20
                    pyxel.line(self.bx, by_off, lx, ly, 2); pyxel.line(lx, ly, lx + (10 * self.b_dir), ly + 30, 0)
                    rx = self.bx - math.cos(t + i) * 40; ry = by_off + math.sin(t + i) * 20
                    pyxel.line(self.bx, by_off, rx, ry, 2); pyxel.line(rx, ry, rx - (10 * self.b_dir), ry + 30, 0)
                pyxel.circ(self.bx, by_off-10, 24, 0); pyxel.circ(self.bx, by_off-10, 20, 2)
                pyxel.circ(self.bx + (16*self.b_dir), by_off-4, 12, 0); pyxel.circ(self.bx + (20*self.b_dir), by_off-8, 2, 8)
                pyxel.circ(self.bx + (24*self.b_dir), by_off-4, 2, 8); pyxel.circ(self.bx + (20*self.b_dir), by_off, 2, 8)

        elif self.level == 6:
            l6_rage = hp_pct <= 0.8
            l6_balkan = hp_pct <= 0.2
            by_off = self.by - 20
            
            walk_cycle = math.sin(pyxel.frame_count * 0.4) * 8 if abs(dist_x) > 30 else 0
            if l6_balkan: self.draw_glow(self.bx, by_off - 40, 60, [0, 1, 5, 12])

            pyxel.rect(self.bx - 24, by_off + 20, 48, 8, 0) 
            arm_col = 1; armor_col = 5 if not l6_rage else 1; glove_col = 5 if not l6_rage else 8
            pyxel.rect(self.bx - 16 - walk_cycle, by_off - 20, 12, 40, armor_col); pyxel.rect(self.bx + 4 + walk_cycle, by_off - 20, 12, 40, armor_col) 
            pyxel.rect(self.bx - 28, by_off - 70, 56, 50, 0); pyxel.rect(self.bx - 24, by_off - 68, 48, 30, armor_col) 
            pyxel.rect(self.bx - 18, by_off - 38, 36, 20, armor_col) 
            
            if l6_rage: 
                pyxel.circ(self.bx - 12, by_off - 50, 2, 8); pyxel.circ(self.bx + 16, by_off - 56, 2, 8)
                pyxel.circ(self.bx - 4, by_off - 40, 2, 8); pyxel.circ(self.bx + 10, by_off - 30, 2, 8)

            pyxel.rect(self.bx - 14, by_off - 92, 28, 24, 0); pyxel.rect(self.bx - 12, by_off - 90, 24, 20, armor_col)
            eye_col = 8 if l6_rage else 13
            b_dir = 1 if dist_x > 0 else -1
            pyxel.rect(self.bx - 8 + (4*b_dir), by_off - 84, 6, 4, eye_col); pyxel.rect(self.bx + 4 + (4*b_dir), by_off - 84, 6, 4, eye_col)

            punch_ext = 40 if getattr(self, 'punch_anim', 0) > 0 else 0
            back_x = self.bx - (20 * b_dir)
            pyxel.rect(back_x - 6, by_off - 64, 12, 30, arm_col); pyxel.rect(back_x - 10, by_off - 44, 20, 20, glove_col) 
            pyxel.rect(back_x - 8, by_off - 42, 16, 16, 2 if not l6_rage else 8)

            front_x = self.bx + (20 * b_dir)
            if punch_ext > 0:
                if b_dir == 1: pyxel.rect(front_x, by_off - 56, punch_ext, 12, arm_col)
                else: pyxel.rect(front_x - punch_ext, by_off - 56, punch_ext, 12, arm_col)
            glove_x = front_x + (punch_ext * b_dir)
            pyxel.rect(glove_x - 12, by_off - 64, 24, 24, glove_col); pyxel.rect(glove_x - 10, by_off - 62, 20, 20, 2 if not l6_rage else 8)

        elif self.level == 7:
            by_off = self.by
            phase_4 = getattr(self, 'boss_action', 0) == 3
            float_y = by_off if phase_4 else by_off + math.sin(t * 0.1) * 6
            
            pyxel.rect(self.bx - 8, float_y - 90, 16, 80, 0); pyxel.rect(self.bx - 6, float_y - 88, 12, 76, 1)
            
            b_dir = 1 if dist_x > 0 else -1
            head_x = self.bx + (6 * b_dir if phase_4 else 0)
            head_y = float_y - 110 if phase_4 else float_y - 100
            pyxel.circ(head_x, head_y, 12, 0); pyxel.circ(head_x, head_y, 10, 13)
            pyxel.circ(head_x - 4 + (b_dir*2), head_y - 2, 2, 8); pyxel.circ(head_x + 4 + (b_dir*2), head_y - 2, 2, 8) 
            pyxel.line(head_x - 8, head_y - 10, head_x - 16, head_y + 10, 0); pyxel.line(head_x + 8, head_y - 10, head_x + 16, head_y + 10, 0)

            if phase_4:
                pyxel.line(self.bx, float_y - 60, self.bx + (30 * b_dir), float_y - 20, 13)
                pyxel.circ(self.bx + (30 * b_dir), float_y - 20, 4, 8) 
                pyxel.line(self.bx, float_y - 20, self.bx - (20 * b_dir), float_y, 13)
            else:
                pyxel.line(self.bx - 8, float_y - 70, self.bx - 24, float_y - 50, 13); pyxel.line(self.bx - 24, float_y - 50, self.bx - 20, float_y - 20, 13)
                pyxel.line(self.bx + 8, float_y - 70, self.bx + 30, float_y - 50, 13); pyxel.line(self.bx + 30, float_y - 50, self.bx + 24, float_y - 30, 13)
                staff_x = self.bx + 24
                pyxel.line(staff_x, float_y + 20, staff_x, float_y - 90, 4)
                pyxel.circ(staff_x, float_y - 94, 8, 7); pyxel.circ(staff_x - 2, float_y - 96, 2, 0); pyxel.circ(staff_x + 2, float_y - 96, 2, 0)
                self.draw_glow(staff_x, float_y - 94, 16, [0, 13, 5])

        if is_frozen:
            ix = self.bx - bw//2; iy = self.by - bh
            pyxel.rectb(ix, iy, bw, bh, 7); pyxel.rectb(ix-2, iy-2, bw+4, bh+4, 12); pyxel.rectb(ix+2, iy+2, bw-4, bh-4, 12)
            pyxel.line(ix, iy, ix+bw, iy+bh, 12); pyxel.line(ix+bw, iy, ix, iy+bh, 12)
            self.text_thick(self.bx-30, iy-20, "FROZEN", 12)

    def draw(self):
        if self.state == 7 and not getattr(self, 'credits_list', None):
            pyxel.cls(0)
            self.draw_text_scaled(20, 20, "DATA RELOAD ERROR", 8)
            self.draw_text_scaled(20, 40, "PLEASE RESTART THE LEVEL", 7)
            return

        if self.shake > 0: pyxel.camera(random.randint(-self.shake, self.shake)//2, random.randint(-self.shake, self.shake)//2)
        else: pyxel.camera(0, 0)
        if self.flash > 0: pyxel.cls(7); return 
        
        if self.state == 7: self.draw_peaceful_background()
        elif self.state == 8: self.draw_story()
        elif self.state == 9:
            pyxel.cls(0)
            self.text_shadow(100, 200, "THE WITCH IS DEFEATED...", 10)
            self.text_shadow(100, 240, "BUT HER MINIONS STILL DWELL IN THE LANDS BETWEEN.", 8)
            self.text_shadow(100, 280, "PLEASE LOAD 'endless.py' TO CLEANSE THE WORLD.", 11)
            self.text_shadow(100, 340, "PRESS SPACE TO RETURN TO MENU", 7)
        else: self.draw_epic_background()
        
        if self.state == 3:
            # Platforms
            for p in getattr(self, 'platforms', []):
                if len(p) >= 8 and p[3] > 0: 
                    pyxel.rect(p[0], p[1], p[2], 20, 0)
                    pyxel.rect(p[0] + 2, p[1] + 2, p[2] - 4, 16, 13)
                    for i in range(p[0] + 4, p[0] + p[2] - 4, 12):
                        pyxel.line(i, p[1] + 4, i, p[1] + 16, 1)

            # Squires
            for s in getattr(self, 'squires', []):
                sx, sy = s[0], s[1]-24
                if len(s) > 2:
                    pyxel.rect(sx - 12, sy, 24, 24, 0) 
                    sq_col = 12 if s[3] == 0 else (8 if s[3] == 1 else 10)
                    pyxel.rect(sx - 10, sy + 2, 20, 20, sq_col)
                    b_dir = s[4]
                    pyxel.rect(sx + (4 * b_dir), sy + 6, 4, 4, 7)
                    if s[3] == 0: pyxel.rect(sx + (12 * b_dir), sy - 8, 4, 20, 7)
                    elif s[3] == 1: pyxel.rect(sx + (8 * b_dir), sy + 12, 12, 4, 0)
                    elif s[3] == 2: 
                        shield_col = 9 if s[6] > 0 else 5
                        pyxel.rect(sx + (12 * b_dir), sy - 4, 4, 28, shield_col)
                else:
                    pyxel.rect(sx - 10, sy, 20, 20, 0)
                    pyxel.rect(sx - 8, sy + 2, 16, 16, 13)

            # Skeletons
            for sk in getattr(self, 'skeletons', []):
                sx, sy = sk[0], sk[1]
                pyxel.rect(sx - 8, sy - 40, 16, 40, 0)
                pyxel.rect(sx - 6, sy - 38, 12, 36, 7)
                pyxel.circ(sx, sy - 44, 8, 7) 
                pyxel.pset(sx - 4, sy - 46, 0); pyxel.pset(sx + 4, sy - 46, 0)
                b_dir = sk[3]
                pyxel.line(sx, sy - 20, sx + (30 * b_dir), sy - 10, 5)

            # Earth spikes
            for sp in getattr(self, 'earth_spikes', []):
                if sp[2] == 0: pyxel.line(sp[0] - 20, FLOOR_Y, sp[0] + 20, FLOOR_Y, 8 + (pyxel.frame_count%2)*2)
                else:
                    pyxel.tri(sp[0] - 24, FLOOR_Y, sp[0] + 24, FLOOR_Y, sp[0], FLOOR_Y - 80, 4) 
                    pyxel.tri(sp[0] - 12, FLOOR_Y, sp[0] + 12, FLOOR_Y, sp[0], FLOOR_Y - 70, 9)

            # Traps & Geysers
            if self.level == 4 or self.level == 7:
                for w in self.geyser_warns: pyxel.rect(w[0] - 30, FLOOR_Y, 60, 8, 9 if self.level==4 else 8)
                for g in self.geysers:
                    h = (30 - g[1]) * 20
                    pyxel.rect(g[0] - 24, FLOOR_Y - h, 48, h, 8) 
                    pyxel.rect(g[0] - 12, FLOOR_Y - h, 24, h, 9) 
                    self.spawn_particles(g[0], FLOOR_Y - h, 9, 5, custom_vy=random.uniform(-8, -4), gravity=True)
            elif getattr(self, 'level', 1) == 5:
                for w in getattr(self, 'web_traps', []): 
                    pyxel.rect(w[0] - 48, w[1] - 6, 96, 12, 11)
                    pyxel.rect(w[0] - 32, w[1] - 10, 64, 20, 3)
                for ts in getattr(self, 'tiny_spiders', []):
                    sx, sy = ts[0], ts[1]
                    pyxel.circ(sx, sy, 4, 0); pyxel.pset(sx, sy - 2, 13); pyxel.pset(sx, sy, 8) 
            
            # Boss Bullets
            for b in self.boss_bullets: 
                if b[4] == "burger":
                    pyxel.circ(b[0], b[1], 10, 0); pyxel.rect(b[0] - 8, b[1] - 6, 16, 4, 4) 
                    pyxel.line(b[0] - 8, b[1] - 2, b[0] + 6, b[1] - 2, 3)
                    pyxel.line(b[0] - 8, b[1], b[0] + 6, b[1], 8) 
                    pyxel.rect(b[0] - 8, b[1] + 2, 16, 4, 0); pyxel.rect(b[0] - 8, b[1] + 6, 16, 4, 4) 
                elif b[4] == "web": pyxel.circ(b[0], b[1], 8, 13); pyxel.circb(b[0], b[1], 6, 6)
                elif b[4] == "shotgun":
                    pyxel.circ(b[0], b[1], 4, 9); pyxel.circ(b[0] - b[2]*0.5, b[1] - b[3]*0.5, 2, 10)
                elif b[4] == "witch_fireball":
                    pyxel.circ(b[0], b[1], 12, 2); pyxel.circ(b[0], b[1], 8, 8); pyxel.pset(b[0], b[1], 10)
                elif b[4] == "witch_freeze":
                    pyxel.circ(b[0], b[1], 8, 1); pyxel.circ(b[0], b[1], 4, 12)
                elif b[4] == "poison":
                    pyxel.circ(b[0], b[1], 16, 3); pyxel.circ(b[0], b[1], 12, 11); pyxel.circb(b[0], b[1], 16, 0)
                else: 
                    pyxel.line(b[0], b[1], b[0] - b[2]*1.2, b[1] - b[3]*1.2, 9)
                    pyxel.rect(b[0] - 4, b[1] - 4, 8, 8, 10)

            # Projectiles
            for p in self.projectiles: 
                if len(p) > 4 and p[4] == "fireball":
                    pyxel.circ(p[0], p[1], 14, 8); pyxel.circ(p[0], p[1], 10, 9); pyxel.circ(p[0], p[1], 6, 10)
                    pyxel.circ(p[0] - p[2], p[1] - p[3], 10, 8); pyxel.circ(p[0] - p[2]*2, p[1] - p[3]*2, 6, 2)
                elif len(p) > 4 and p[4] == "freeze":
                    pyxel.circ(p[0], p[1], 6, 12); pyxel.pset(p[0], p[1], 7)
                else:
                    ex = p[0] - p[2]*2; ey = p[1] - p[3]*2
                    pyxel.line(p[0], p[1], ex, ey, 11); pyxel.line(p[0] - 2, p[1], ex - 2, ey, 3)
                    pyxel.line(p[0] + 2, p[1], ex + 2, ey, 3); pyxel.line(p[0], p[1] - 2, ex, ey - 2, 3)
                    pyxel.line(p[0], p[1] + 2, ex, ey + 2, 3); pyxel.circ(p[0], p[1], 4, 7)

            for b in getattr(self, 'bombs', []): 
                pyxel.circ(b[0], b[1], 8, 8); pyxel.circ(b[0], b[1], 4, 10) 
            for e in self.explosions: 
                pyxel.circb(e[0], e[1], e[2], e[4])
                if e[2] > 12: pyxel.circb(e[0], e[1], e[2] - 8, e[5]) 
            for p in self.particles: p.draw()

            # Draw Boss & Player
            self.draw_detailed_mage()
            self.draw_detailed_boss()

            for f in self.flames: pyxel.circ(f[0], f[1], max(4, f[4] // 3), 10 if f[4] > 35 else 9)
            
            for sh in getattr(self, 'soul_heads', []):
                pyxel.circ(sh[0], sh[1], 8, 13); pyxel.circ(sh[0], sh[1], 6, 7)
                pyxel.pset(sh[0] - 2, sh[1] - 2, 0); pyxel.pset(sh[0] + 2, sh[1] - 2, 0)
                self.draw_glow(sh[0] - sh[2]*2, sh[1] - sh[3]*2, 8, [0, 5, 13])
            
            # Witch Darkness Blindness Overlay (Level 7)
            if getattr(self, 'witch_darkness', False):
                cx, cy = int(self.px + 24), int(self.py + 32); r = 50 
                pyxel.rect(0, 0, SCREEN_WIDTH, cy - r, 0) 
                pyxel.rect(0, cy + r, SCREEN_WIDTH, SCREEN_HEIGHT - (cy + r), 0) 
                pyxel.rect(0, cy - r, cx - r, r * 2, 0) 
                pyxel.rect(cx + r, cy - r, SCREEN_WIDTH - (cx + r), r * 2, 0) 
                for i in range(r, r + 40): pyxel.circb(cx, cy, i, 0)

            # TUTORIAL BOX DRAWING
            if self.level == 0:
                pyxel.rectb(78, 98, 356, 64, 7)
                if self.tut_step == 0:
                    self.text_thick(100, 110, "TUTORIAL: USE A/D TO MOVE", 7)
                    self.text_thick(100, 130, "AND SPACEBAR TO JUMP", 7)
                elif self.tut_step == 1:
                    self.text_thick(100, 110, "AIM WITH THE MOUSE CURSOR", 7)
                    self.text_thick(100, 130, "AND LEFT-CLICK TO FIRE BASIC SPELLS", 7)
                elif self.tut_step == 2:
                    self.text_thick(100, 110, "TEMPORARY FIREBALL Spell Unlocked!", 10)
                    self.text_thick(100, 130, "PRESS Q TO BLAST THE TARGET", 7)
                elif self.tut_step == 3:
                    self.text_thick(100, 120, "DEPLOYING WARLOCK TO THE FRONT LINES...", 11)
            
            # Draw Floating Texts (Damage numbers, heals, etc.)
            for ft in self.floating_texts:
                self.text_shadow(ft.x, ft.y, ft.text, ft.color)
        
        t = pyxel.frame_count
        
        if self.state == 0:
            self.draw_text_scaled(70, 120, "THE MALEVOLENT WITCH", (t // 8) % 15 + 1, scale=3)
            
            main_opt = "A) DEPLOY WARLOCK" if self.has_seen_story else "A) BEGIN THE TALE"
            self.text_shadow(120, 220, main_opt, 7)
            
            self.text_shadow(120, 250, "B) UPGRADES", 7)
            self.text_shadow(120, 280, "C) DEV PANEL", 7)
            self.text_shadow(120, 310, "D) SETTINGS", 10)
            self.text_shadow(120, 340, "E) TUTORIAL", 11)
            if self.game_beaten:
                self.text_shadow(120, 370, "F) CLEAN THE WORLD (ENDLESS)", 10)
            else:
                self.text_shadow(120, 370, "F) CLEAN THE WORLD [LOCKED - DEFEAT WITCH]", 5)
            
        elif self.state == 7:
            current_credits = self.credits_list if self.credits_list else []
            for i in range(len(current_credits)):
                line = current_credits[i]
                if line is None: continue 
                x = SCREEN_WIDTH//2 - (len(line) * 4); y = int(self.credits_y + i * 80)
                
                if -20 < y < SCREEN_HEIGHT:
                    line_color = 7 
                    if "Jashan" in line and "Jashan" != line:
                        parts = line.split("Jashan")
                        self.draw_text_scaled(x, y, parts[0], 5)
                        self.draw_text_scaled(x + len(parts[0])*8, y, "Jashan", 10) 
                        continue
                    elif "Tony" in line and "Tony" != line:
                        parts = line.split("Tony")
                        self.draw_text_scaled(x, y, parts[0], 13)
                        self.draw_text_scaled(x + len(parts[0])*8, y, "Tony", 11) 
                        continue
                    elif "COMMANDER" in line or "PROFILE" in line or "LOG" in line: line_color = 13 
                    elif "THE MALEVOLENT WITCH" == line: line_color = 8 
                    self.text_thick(x, y, line, line_color)
            
        elif self.state == 6:
            self.text_shadow(40, 40, "--- AUDIO SETTINGS ---", 11)
            self.text_shadow(40, 100, "MUSIC VOLUME", 7); self.text_shadow(40, 130, f"< {self.music_vol} / 7 >", 10)
            self.text_shadow(160, 130, "[UP / DOWN]", 5); self.text_shadow(40, 190, "SFX VOLUME", 7)
            self.text_shadow(40, 220, f"< {self.sfx_vol} / 7 >", 10); self.text_shadow(160, 220, "[LEFT / RIGHT]", 5)
            self.text_shadow(40, 440, "BACKSPACE TO EXIT", 6)
            
        elif self.state == 1:
            self.text_shadow(40, 40, f"COINS: {self.coins}", 10); self.text_shadow(40, 80, "-- CORE STATS --", 6)
            self.text_shadow(40, 110, f"1) DMG LVL {self.damage_lv} ({self.damage_lv*50}c)", 7)
            self.text_shadow(40, 140, f"2) SPD LVL {self.proj_speed_lv} ({self.proj_speed_lv*30}c)", 7)
            self.text_shadow(40, 170, f"3) MAX HP LVL {self.hp_lv} ({self.hp_lv*40}c)", 7)
            self.text_shadow(40, 220, f"-- SPELLS (ACTIVE: {self.active_spell or 'NONE'}) --", 6)
            
            def spell_txt(num, name, cost, y):
                state = "[ACTIVE]" if self.active_spell == name else ("[OWNED]" if name in self.unlocked_spells else f"({cost}c)")
                col = 10 if self.active_spell == name else (7 if name in self.unlocked_spells else 5)
                self.text_shadow(40, y, f"{num}) {name} {state}", col)
                
            spell_txt(4, "Teleport", 100, 250); spell_txt(5, "Fireball", 150, 280)
            spell_txt(6, "Freeze", 150, 310); spell_txt(7, "Heal", 200, 340)
            self.text_shadow(40, 440, "BACKSPACE TO EXIT", 6)
            
        elif self.state == 2:
            self.text_shadow(40, 40, "DEV UNLOCKED (GOD MODE)", 11); self.text_shadow(40, 90, ">>> WARP CONSOLE <<<", 10)
            self.text_shadow(40, 120, "PRESS NUMBER KEYS [1] TO [7]", 7); self.text_shadow(40, 140, "ON YOUR KEYBOARD TO", 7)
            self.text_shadow(40, 160, "DIRECTLY JUMP TO THAT LEVEL.", 7)
            self.text_shadow(40, 190, "PRESS [the number 8] TO UNLOCK ENDLESS", 10)
            self.text_shadow(40, 220, f"I) IMMORTAL: {self.immortal}", 7 if self.immortal else 5)
            self.text_shadow(40, 280, "DAMAGE MULTIPLIER", 7)
            mult = int(1 + (self.dev_dmg_slider * 99))
            self.text_shadow(40, 300, f"CURRENT: {mult}x", 10)
            pyxel.rect(40, 330, 200, 12, 1); pyxel.rect(42, 332, int(self.dev_dmg_slider * 196), 8, 8) 
            pyxel.rect(40 + int(self.dev_dmg_slider * 196) - 6, 324, 12, 24, 7) 
            
            self.text_shadow(40, 360, "CLICK SPELL TO UNLOCK:", 7)
            spells = ["Teleport", "Fireball", "Freeze", "Heal"]
            for i, sp in enumerate(spells):
                col = 10 if self.active_spell == sp else 5
                self.text_shadow(40 + i*90, 390, sp[:4], col)
            self.text_shadow(40, 440, "BACKSPACE TO EXIT", 6)
            
        elif self.state == 3:
            pyxel.rect(20, 20, 160, 16, 1)
            pyxel.rect(24, 24, (max(0, self.p_hp_disp)/self.max_hp)*152, 8, 11 if not self.p_poisoned else 3)
            self.text_shadow(24, 40, f"HP: {int(self.p_hp)}/{self.max_hp}", 11 if not self.p_poisoned else 3)
            
            if self.active_spell:
                cd_ratio = 0 if self.spell_cooldown <= 0 else (self.spell_cooldown / getattr(self, 'max_cd', 60))
                pyxel.rect(20, 60, 160, 8, 1); pyxel.rect(24, 62, (1.0 - cd_ratio)*152, 4, 10 if cd_ratio == 0 else 5)
                self.text_shadow(24, 72, f"SPELL: {self.active_spell}", 10 if cd_ratio == 0 else 5)

            pyxel.rect(SCREEN_WIDTH - 180, 20, 160, 16, 1)
            
            if getattr(self, 'level', 1) == 7 and getattr(self, 'boss_action', 0) == 1:
                fill_ratio = min(1.0, self.skeletons_killed / 20.0)
                pyxel.rect(SCREEN_WIDTH - 176, 24, fill_ratio * 152, 8, 7)
                self.text_shadow(SCREEN_WIDTH - 176, 40, f"SKULLS: {self.skeletons_killed}/20", 7)
            else:
                pyxel.rect(SCREEN_WIDTH - 176, 24, (max(0, self.boss_hp_disp)/self.boss_max_hp)*152, 8, 8 if getattr(self, 'level', 1) != 7 else 13)
                
                if getattr(self, 'level', 1) == 5 and getattr(self, 'boss_action', 0) == 1:
                    self.text_shadow(SCREEN_WIDTH - 176, 40, f"SURVIVE: {max(0, 20 - (self.swarm_timer//60))}s", 8)
                elif getattr(self, 'level', 1) == 6 and (self.boss_hp / self.boss_max_hp) <= 0.2:
                    self.text_shadow(SCREEN_WIDTH - 176, 40, "ALPHA WOLF", 8)
                elif getattr(self, 'level', 1) == 0:
                    self.text_shadow(SCREEN_WIDTH - 176, 40, "LORD OF HAY, WOODY", 8)
                else: 
                    self.text_shadow(SCREEN_WIDTH - 176, 40, f"PHASE {self.level}", 8 if getattr(self, 'level', 1) != 7 else 13)
            
        elif self.state == 4: self.text_shadow(200, 240, "GAME OVER", 8)
        elif self.state == 5: 
            if getattr(self, 'level', 1) == 7: self.text_shadow(SCREEN_WIDTH//2 - 80, 220, "WITCH SLAIN", 11)
            else: self.text_shadow(SCREEN_WIDTH//2 - 120, 220, "KNIGHT VANQUISHED", 11)
            self.text_shadow(SCREEN_WIDTH//2 - 150, 260, "PRESS SPACE TO CONTINUE", 7)

# Ignite the Engine
Game()