import sys

MIN_VER = (3, 9)

if sys.version_info[:2] < MIN_VER:
    sys.exit(
        "This game requires Python {}.{}.".format(*MIN_VER)
    )

import arcade
import arcade.camera
import os
import random
import math
from typing import Optional, List

# Constants
SCREEN_WIDTH = 1300
SCREEN_HEIGHT = 660
SCREEN_TITLE = "LUNAR ANOMALY"

# Scaling & Physics
CHARACTER_SCALING = 0.31
TILE_SCALING = 0.5
COIN_SCALING = 0.4
PLAYER_MOVEMENT_SPEED = 6
GRAVITY = 1.2
PLAYER_JUMP_SPEED = 22
PLAYER_DRAG = 0.15

# MAW & LEECH CONSTANTS
MAW_SPEED = 1.6
PARASITE_SCALING = 0.12

# Camera Lerp
CAMERA_SPEED = 0.08 # Slightly slower for that heavy feel
DEATH_HEIGHT = -150
ENERGY_DECAY = 0.08 # 2x slower
PLANT_SCALING = 0.07 # Smaller

# Assets paths
BASE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets")
SPRITES_PATH = os.path.join(BASE_PATH, "Sprites")
TILES_PATH = os.path.join(SPRITES_PATH, "Tiles", "Double")
CHARACTERS_PATH = os.path.join(SPRITES_PATH, "Characters", "Double")
BG_PATH = os.path.join(SPRITES_PATH, "Backgrounds", "Double")
SKY_IMG = os.path.join(BASE_PATH, "sky.png")

def load_texture_pair(filename):
    t1 = arcade.load_texture(filename)
    return [t1, t1.flip_horizontally()]

class Particle(arcade.Sprite):
    def __init__(self, x, y, dx, dy, color=arcade.color.WHITE, lifetime=30):
        # Initialize without keywords to appease strict LSPs
        super().__init__()
        self.texture = arcade.make_soft_circle_texture(10, color)
        self.center_x = x
        self.center_y = y
        self.dx = dx
        self.dy = dy
        self.lifetime = lifetime
        self.max_lifetime = lifetime
        self.alpha = 255
        self.scale = 0.4

    def update(self, delta_time: float = 1/60):
        self.center_x += self.dx
        self.center_y += self.dy
        self.lifetime -= 1
        self.alpha = int(255 * (self.lifetime / self.max_lifetime))
        if self.lifetime <= 0:
            self.remove_from_sprite_lists()

class SlimeEnemy(arcade.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.rest_texture = arcade.load_texture(os.path.join(BASE_PATH, "Sprites", "Enemies", "Default", "slime_normal_rest.png"))
        self.walk_texture = arcade.load_texture(os.path.join(BASE_PATH, "Sprites", "Enemies", "Default", "slime_normal_walk_a.png"))
        self.texture = self.rest_texture
        self.scale = 1.0
        self.center_x = x
        self.center_y = y

    def update(self, delta_time: float = 1/60):
        super().update()
        # Visual feedback: if moving (knocked?) change texture
        if abs(self.change_x) > 0.1 or abs(self.change_y) > 0.1:
            self.texture = self.walk_texture
        else:
            self.texture = self.rest_texture

class PlayerCharacter(arcade.Sprite):
    def __init__(self):
        super().__init__()
        
        self.idle_texture_pair = load_texture_pair(os.path.join(CHARACTERS_PATH, "character_green_idle.png"))
        self.jump_texture_pair = load_texture_pair(os.path.join(CHARACTERS_PATH, "character_green_jump.png"))
        self.walk_textures = []
        for i in ["a", "b"]:
            self.walk_textures.append(load_texture_pair(os.path.join(CHARACTERS_PATH, f"character_green_walk_{i}.png")))
        
        self.texture = self.idle_texture_pair[0]
        self.scale = CHARACTER_SCALING
        self.cur_texture = 0
        self.character_face_direction = 0
        
        # Internal scale for squash/stretch
        self.base_scale_x = CHARACTER_SCALING
        self.base_scale_y = CHARACTER_SCALING
        self.scale_x = CHARACTER_SCALING
        self.scale_y = CHARACTER_SCALING

    def update_animation(self, delta_time: float = 1/60):
        if self.change_x < -0.1: self.character_face_direction = 1
        elif self.change_x > 0.1: self.character_face_direction = 0

        if self.change_y != 0:
            self.texture = self.jump_texture_pair[self.character_face_direction]
        elif abs(self.change_x) < 0.1:
            self.texture = self.idle_texture_pair[self.character_face_direction]
        else:
            self.cur_texture += 1
            frame = (self.cur_texture // 4) % len(self.walk_textures)
            self.texture = self.walk_textures[frame][self.character_face_direction]

        # Squash and stretch lerp
        self.scale_x = arcade.math.lerp(self.scale_x, self.base_scale_x, 0.15)
        self.scale_y = arcade.math.lerp(self.scale_y, self.base_scale_y, 0.15)

class MyGame(arcade.Window):
    def __init__(self):
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)
        
        # SpriteLists
        self.wall_list: arcade.SpriteList = arcade.SpriteList(use_spatial_hash=True)
        self.bg_list_far: arcade.SpriteList = arcade.SpriteList()
        self.player_list: arcade.SpriteList = arcade.SpriteList()
        self.item_list: arcade.SpriteList = arcade.SpriteList()
        self.decor_list: arcade.SpriteList = arcade.SpriteList()
        self.particle_list: arcade.SpriteList = arcade.SpriteList()
        self.leech_list: arcade.SpriteList = arcade.SpriteList()
        
        self.player_sprite = PlayerCharacter()
        self.maw_x = -500.0 # Force float
        self.physics_engine: Optional[arcade.PhysicsEnginePlatformer] = None
        self.energy = 1.0
        self.display_energy = 1.0 # For smooth bar movement
        self.stun_timer = 0.0
        self.game_over = False
        self.won = False
        self.winning_sequence = False
        self.win_delay = 0.0
        self.timer = 0.0
        self.finish_time = 0.0
        self.max_dist = 0
        self.menu_active = True # Game starts at menu
        self.time_accum = 0.0 # For animations
        self.was_on_ground = True # For landing effects
        self.left_pressed = False
        self.right_pressed = False
        # Cameras
        self.camera = arcade.camera.Camera2D()
        self.bg_camera_far = arcade.camera.Camera2D()
        self.gui_camera = arcade.camera.Camera2D()
        
        # System cursors
        self.cursor_hand = self.get_system_mouse_cursor(self.CURSOR_HAND)
        self.cursor_default = self.get_system_mouse_cursor(self.CURSOR_DEFAULT)
        
        self.bg_color = (10, 8, 18) # Deep void

        # AAA UI Theme
        # Title with thicker glow
        self.title_label = arcade.Text(
            "LUNAR ANOMALY", SCREEN_WIDTH/2, SCREEN_HEIGHT/2 + 100,
            arcade.color.WHITE, 86, font_name="Arial", anchor_x="center", bold=True
        )
        self.title_glow = arcade.Text(
            "LUNAR ANOMALY", SCREEN_WIDTH/2, SCREEN_HEIGHT/2 + 100,
            (0, 255, 200, 40), 86, font_name="Arial", anchor_x="center", bold=True
        )
        self.btn_label = arcade.Text(
            "START", SCREEN_WIDTH/2, SCREEN_HEIGHT/2 - 50,
            (200, 200, 210), 22, font_name="Georgia", anchor_x="center", anchor_y="center"
        )
        self.resistance_label = arcade.Text(
            "NIGHT RESISTANCE", 40, SCREEN_HEIGHT - 27, (180, 200, 220), 11, font_name="Georgia", bold=True
        )
        self.percentage_label = arcade.Text(
            "", SCREEN_WIDTH - 45, SCREEN_HEIGHT - 53, (255, 255, 255), 15, font_name="Arial", anchor_x="right", anchor_y="center", bold=True
        )
        self.journey_label = arcade.Text(
            "", SCREEN_WIDTH - 35, SCREEN_HEIGHT - 85, (200, 180, 230), 12, font_name="Arial", anchor_x="right", bold=True
        )
        self.time_label = arcade.Text(
            "", SCREEN_WIDTH - 35, SCREEN_HEIGHT - 105, (150, 140, 180), 11, font_name="Arial", anchor_x="right"
        )

    def setup(self):
        self.player_list.clear()
        self.wall_list.clear()
        self.item_list.clear()
        self.decor_list.clear()
        self.bg_list_far.clear()
        self.particle_list.clear()
        self.leech_list.clear()
        self.maw_x = -500.0
        self.shake_jumps = 0
        self.mouse_x, self.mouse_y = 0, 0
        self.set_mouse_cursor(self.cursor_default)

        # Set up player
        self.player_sprite = PlayerCharacter()
        self.player_sprite.center_x = 200
        self.player_sprite.center_y = 500
        self.player_list.append(self.player_sprite)

        self._setup_background()
        self._generate_map()

        self.physics_engine = arcade.PhysicsEnginePlatformer(
            self.player_sprite, gravity_constant=GRAVITY, walls=self.wall_list
        )
        self.energy = 1.0
        self.display_energy = 1.0
        self.game_over = False
        self.won = False
        self.winning_sequence = False
        self.win_delay = 0.0
        self.timer = 0.0
        self.max_dist = 0
        self.was_on_ground = True
        self.left_pressed = False
        self.right_pressed = False
        # self.menu_active = False # Start actual game play - REMOVED

    def respawn(self):
        """Reset player to start without laggy setup."""
        self.player_sprite.center_x = 200
        self.player_sprite.center_y = 500
        self.player_sprite.change_x = 0
        self.player_sprite.change_y = 0
        
        # Snap camera instantly
        new_x = max(self.width/2, self.player_sprite.center_x)
        self.camera.position = (new_x, self.player_sprite.center_y)
        
        # Flash or particles here if we wanted, but keeping it smooth for now.
        self.spawn_dust(self.player_sprite.center_x, self.player_sprite.center_y, dy=5, count=20)

    def _setup_background(self):
        # Layer 1: Custom Sky
        s = arcade.Sprite(SKY_IMG)
        # Fit to window width
        s.width = SCREEN_WIDTH
        s.height = SCREEN_HEIGHT
        s.center_x = SCREEN_WIDTH / 2
        s.center_y = SCREEN_HEIGHT / 2
        self.bg_list_far.append(s)

    def _generate_map(self):
        map_width = 300
        chunk_size = 6
        
        purple_main = os.path.join(TILES_PATH, "terrain_purple_block.png")
        purple_top = os.path.join(TILES_PATH, "terrain_purple_block_top.png")
        purple_top_left = os.path.join(TILES_PATH, "terrain_purple_block_top_left.png")
        purple_top_right = os.path.join(TILES_PATH, "terrain_purple_block_top_right.png")
        purple_center = os.path.join(TILES_PATH, "terrain_purple_block_center.png")
        purple_left = os.path.join(TILES_PATH, "terrain_purple_block_left.png")
        purple_right = os.path.join(TILES_PATH, "terrain_purple_block_right.png")
        flag_img = os.path.join(TILES_PATH, "flag_red_a.png")
        water_top_img = os.path.join(TILES_PATH, "water_top.png")
        water_img = os.path.join(TILES_PATH, "water.png")
        
        plant_img = os.path.join(BASE_PATH, "plant.png")
        tile_width = 128 * TILE_SCALING
        
        random.seed(123)
        
        # 1. Pre-calculate heights with progressive difficulty
        heights = []
        h = 4
        for x in range(map_width):
            progress = x / map_width
            
            # Difficulty scales over x
            gap_threshold = 0.02 + (0.18 * progress) # Gaps become more frequent
            h_var = int(1 + (3 * progress)) # Heights vary more wildly
            
            if x % 7 == 0:
                h = max(1, min(9, h + random.randint(-h_var, h_var)))
            
            # Create gaps (represented by -1)
            if random.random() < gap_threshold and x > 10:
                heights.append(-1)
            else:
                heights.append(h)

        # 2. Place ground tiles based on neighbors
        water_start_x = map_width - 30
        for x in range(water_start_x): # Stop BEFORE water to prevent walking on bottom
            h_curr = heights[x]
            if h_curr == -1: # It's a gap!
                continue
                
            h_prev = heights[x-1] if x > 0 else -1
            h_next = heights[x+1] if x < map_width - 1 else -1
            
            # Treat gaps as very low for texture selection
            hp_comp = h_prev if h_prev != -1 else -10
            hn_comp = h_next if h_next != -1 else -10

            for y in range(-3, h_curr + 1):
                img = None
                
                if y == h_curr:
                    # Top-most layer
                    if h_curr > hp_comp: img = purple_top_left
                    elif h_curr > hn_comp: img = purple_top_right
                    else: img = purple_top
                else:
                    # Sub-surface layers
                    if x == 0 or y > hp_comp: img = purple_left
                    elif x == map_width - 1 or y > hn_comp: img = purple_right
                    else: img = purple_center
                
                if img:
                    wall = arcade.Sprite(img, TILE_SCALING)
                    wall.center_x = x * tile_width
                    wall.center_y = y * tile_width + (tile_width / 2)
                    self.wall_list.append(wall)
                    
                    # Spawn Slime on top of random ground tiles
                    if y == h_curr and random.random() < 0.05:
                         slime = SlimeEnemy(wall.center_x, wall.top + 32)
                         self.leech_list.append(slime)

            # Parkour / Floating chunks (Slightly rarer as difficulty increases)
            if x < map_width - 35: # Stop early to prepare for the leap
                if x % 12 == 6 and random.random() > (x/map_width * 0.4):
                     p_y = h_curr + 4
                     for i in range(2 + random.randint(0,2)):
                         pw = arcade.Sprite(purple_main, TILE_SCALING)
                         pw.center_x = (x + i) * tile_width
                         pw.center_y = p_y * tile_width
                         self.wall_list.append(pw)
                         if i == 1:
                             plant = arcade.Sprite(plant_img, PLANT_SCALING)
                             plant.center_x = pw.center_x
                             plant.center_y = pw.center_y + 60
                             self.item_list.append(plant)
                             
                             # Removed Leech spawning on floating platforms

        # Place the Final Flag on the last solid block
        flag_x = (water_start_x - 1) * tile_width
        flag_y = (heights[water_start_x - 1] + 1) * tile_width + (tile_width / 2)
        flag = arcade.Sprite(flag_img, TILE_SCALING)
        flag.center_x = flag_x
        flag.center_y = flag_y
        flag.properties["is_goal"] = True
        self.decor_list.append(flag)

        # Generate Water Body
        water_start_x = map_width - 30
        for x in range(water_start_x, map_width + 15):
            for y in range(-3, 2): # Water surface at y=1
                img = water_top_img if y == 1 else water_img
                w = arcade.Sprite(img, TILE_SCALING)
                w.center_x = x * tile_width
                w.center_y = y * tile_width + (tile_width / 2)
                if y == 1:
                    w.properties["is_water"] = True
                self.item_list.append(w)
    def trigger_death(self):
        if self.game_over: return
        self.game_over = True
        self.player_sprite.visible = False
        # Massive Spark Shower
        px, py = self.player_sprite.center_x, self.player_sprite.center_y
        for _ in range(250):
            # Mint/Cyan sparks flying explosively outward
            color = random.choice([arcade.color.MINT_GREEN, arcade.color.ELECTRIC_CYAN, arcade.color.AQUAMARINE, arcade.color.WHITE])
            p = Particle(px, py, random.uniform(-15, 15), random.uniform(-5, 20), color=color, lifetime=random.randint(40, 90))
            p.scale = random.uniform(0.4, 1.2)
            self.particle_list.append(p)

    def spawn_dust(self, x, y, dy=2, count=5, color=arcade.color.WHITE):
        for _ in range(count):
            p = Particle(x, y, random.uniform(-1.5, 1.5), random.uniform(0, dy), color=color, lifetime=random.randint(20, 40))
            self.particle_list.append(p)

    def on_draw(self):
        self.clear()
        
        # 1. Background
        self.gui_camera.use()
        self.bg_list_far.draw()
        
        if self.menu_active:
            # --- AAA START MENU ---
            # Pulse math for smooth bobbing
            pulse = math.sin(self.time_accum * 2)
            
            # Subtle gradient overlay
            arcade.draw_lrbt_rectangle_filled(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT, (5, 5, 15, 245)) 
            
            # Title (Animated Bobbing + Thicker Glow)
            tx, ty = SCREEN_WIDTH / 2, (SCREEN_HEIGHT / 2 + 100) + (pulse * 5)
            self.title_label.position = (tx, ty)
            for ox, oy in [(-3,0), (3,0), (0,-3), (0,3), (-2,-2), (2,2), (-4,-4), (4,4), (-5,0), (5,0)]:
                self.title_glow.position = (tx + ox, ty + oy)
                self.title_glow.draw()
            self.title_label.draw()

            # Start Button
            btn_w, btn_h = 200, 60
            bx, by = SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 - 50
            
            # Change mouse cursor over button
            is_hovering = abs(self.mouse_x - bx) < btn_w/2 and abs(self.mouse_y - by) < btn_h/2
            if is_hovering: self.set_mouse_cursor(self.cursor_hand)
            else: self.set_mouse_cursor(self.cursor_default)
            
            # Box
            arcade.draw_lrbt_rectangle_filled(bx - btn_w/2, bx + btn_w/2, by - btn_h/2, by + btn_h/2, (50, 50, 70))
            arcade.draw_lrbt_rectangle_outline(bx - btn_w/2, bx + btn_w/2, by - btn_h/2, by + btn_h/2, (120, 120, 150), 2)
            
            # Button Text
            self.btn_label.draw()
            
            return

        # 2. Game Layer
        self.camera.use()
        self.wall_list.draw()
        self.decor_list.draw()
        
        self.item_list.draw()
        self.leech_list.draw()
        self.player_list.draw()
        self.particle_list.draw()

        # [The Maw] - Foggy Storm Effect
        # Draw many layers of semi-transparent rectangles
        for i in range(25):
             alpha = int(140 * (1.0 - i/25.0))
             # Shift x slightly based on height-ish but mostly just layering
             # Jitter for billowing effect
             jitter = math.sin(self.time_accum * 1.5 + i * 0.5) * 20
             rect_x = self.maw_x - (i * 20) + jitter
             # Dark atmospheric color
             arcade.draw_lrbt_rectangle_filled(rect_x - 2500, rect_x, -1000, 3000, (10, 15, 25, alpha))
        
        # Lighting Overlay (Vignette)
        self.gui_camera.use()
        arcade.draw_lrbt_rectangle_filled(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT, (0, 0, 0, 60))
        
        # Modern Energy Bar Styling
        bar_width = 280
        bar_height = 28
        bar_x = 40
        bar_y = SCREEN_HEIGHT - 65
        
        # 1. Soft Container (Outer Shadow/Glow)
        arcade.draw_lrbt_rectangle_filled(bar_x - 5, bar_x + bar_width + 5, bar_y - 5, bar_y + bar_height + 5, (20, 20, 25, 100))
        
        # 2. Main Background (Deep Grey-Purple)
        arcade.draw_lrbt_rectangle_filled(bar_x, bar_x + bar_width, bar_y, bar_y + bar_height, (35, 30, 45, 230))
        
        # 3. Filling (Lerped Energy)
        fill_width = bar_width * max(0.0, self.display_energy)
        if fill_width > 0:
            # Main color: Soft Mint to Deep Sea Green
            main_color = (int(50 * (1-self.display_energy)), int(220 * self.display_energy + 35), int(180 * self.display_energy), 255)
            arcade.draw_lrbt_rectangle_filled(bar_x, bar_x + fill_width, bar_y, bar_y + bar_height, main_color)
            
            # 4. Animated Fluid Stripes (Modern Cartoonistic effect)
            stripe_gap = 45
            stripe_width = 15
            offset = (self.time_accum * 60) % stripe_gap
            
            for i in range(-50, int(fill_width), stripe_gap):
                sx = bar_x + i + offset
                # Polygons for slanted, sharp stripes
                points = [
                    (sx, bar_y),
                    (sx + stripe_width, bar_y),
                    (sx + stripe_width + 10, bar_y + bar_height),
                    (sx + 10, bar_y + bar_height)
                ]
                # Filter points to stay within fill boundary roughly
                safe_points = []
                for px, py in points:
                    safe_points.append((max(bar_x, min(bar_x + fill_width, px)), py))
                
                arcade.draw_polygon_filled(safe_points, (200, 255, 220, 60))

        # 5. Glossy Overlay (Curved sheen effect)
        arcade.draw_lrbt_rectangle_filled(bar_x, bar_x + bar_width, bar_y + bar_height/2, bar_y + bar_height, (255, 255, 255, 20))
        
        # 6. Outline
        arcade.draw_lrbt_rectangle_outline(bar_x, bar_x + bar_width, bar_y, bar_y + bar_height, (130, 130, 160, 150), 3)

        # Small label for the bar
        self.resistance_label.draw()

        # 7. Distance & Timer (Top Right - Purple Bar)
        blocks_travelled = int(self.max_dist / 64)
        blocks_travelled = min(blocks_travelled, 269) # Cap at the final flag block
        pct = min(100.0, (blocks_travelled / 269.0) * 100.0)
        
        self.percentage_label.text = f"{int(pct)}%"
        self.journey_label.text = f"BLOCKS: {blocks_travelled} / 269"
        self.time_label.text = f"TIME: {self.timer:.1f}s"
        
        pbar_width = 200
        pbar_height = 26
        pbar_x = SCREEN_WIDTH - 35 - pbar_width
        pbar_y = SCREEN_HEIGHT - 66
        
        # Soft Container & Main Background
        arcade.draw_lrbt_rectangle_filled(pbar_x - 4, pbar_x + pbar_width + 4, pbar_y - 4, pbar_y + pbar_height + 4, (15, 10, 25, 100))
        arcade.draw_lrbt_rectangle_filled(pbar_x, pbar_x + pbar_width, pbar_y, pbar_y + pbar_height, (35, 25, 45, 230))
        
        # Filling
        pfill = pbar_width * (pct / 100.0)
        if pfill > 0:
            fill_color = (int(130 * (pct/100)), int(50), int(160 + 95 * (pct/100)), 255)
            arcade.draw_lrbt_rectangle_filled(pbar_x, pbar_x + pfill, pbar_y, pbar_y + pbar_height, fill_color)
            
            # Stripes
            sgap, swid = 30, 10
            off = (self.time_accum * 50) % sgap
            for i in range(-30, int(pfill), sgap):
                sx = pbar_x + i + off
                pts = [(sx, pbar_y), (sx + swid, pbar_y), (sx + swid + 8, pbar_y + pbar_height), (sx + 8, pbar_y + pbar_height)]
                spts = [(max(pbar_x, min(pbar_x + pfill, px)), py) for px, py in pts]
                if len(spts) == 4:
                    arcade.draw_polygon_filled(spts, (220, 150, 255, 60))
                
        # Gloss & Outline
        arcade.draw_lrbt_rectangle_filled(pbar_x, pbar_x + pbar_width, pbar_y + pbar_height/2, pbar_y + pbar_height, (255, 255, 255, 20))
        arcade.draw_lrbt_rectangle_outline(pbar_x, pbar_x + pbar_width, pbar_y, pbar_y + pbar_height, (140, 110, 170, 150), 2)
        
        self.percentage_label.draw()
        self.journey_label.draw()
        self.time_label.draw()

        # Game Over Overlay
        if self.game_over:
           # Soft, non-gradient overlay
            arcade.draw_lrbt_rectangle_filled(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT, (20, 20, 30, 180))
            
            # YOU DIED Text
            arcade.draw_text("YOU DIED", SCREEN_WIDTH/2, SCREEN_HEIGHT/2 + 50, (230, 230, 235), 48, 
                             font_name="Georgia", anchor_x="center", anchor_y="center")
            
            # Restart Button
            btn_w, btn_h = 200, 60
            bx = SCREEN_WIDTH/2
            by = SCREEN_HEIGHT/2 - 50
            
            # Box
            arcade.draw_lrbt_rectangle_filled(bx - btn_w/2, bx + btn_w/2, by - btn_h/2, by + btn_h/2, (50, 50, 70))
            arcade.draw_lrbt_rectangle_outline(bx - btn_w/2, bx + btn_w/2, by - btn_h/2, by + btn_h/2, (120, 120, 150), 2)
            
            # Button Text
            arcade.draw_text("RESTART", bx, by, (200, 200, 210), 22, 
                             font_name="Georgia", anchor_x="center", anchor_y="center")

        # Win Overlay
        if self.won:
            arcade.draw_lrbt_rectangle_filled(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT, (10, 40, 30, 200)) # Special green tint
            
            arcade.draw_text("YOU MADE IT!", SCREEN_WIDTH/2, SCREEN_HEIGHT/2 + 70, (200, 255, 210), 48, 
                             font_name="Georgia", anchor_x="center", anchor_y="center", bold=True)
            
            blocks_travelled = min(int(self.max_dist / 64), 269)
            finish_msg = f"Time: {self.finish_time:.2f}s | Distance: {blocks_travelled} Blocks"
            arcade.draw_text(finish_msg, SCREEN_WIDTH/2, SCREEN_HEIGHT/2, (180, 220, 190), 24, 
                             font_name="Georgia", anchor_x="center", anchor_y="center")
            
            arcade.draw_text("Press SPACE to Play Again", SCREEN_WIDTH/2, SCREEN_HEIGHT/2 - 70, (150, 180, 160), 16, 
                             font_name="Georgia", anchor_x="center", anchor_y="center")
    def on_update(self, delta_time):
        self.time_accum += delta_time
        if self.menu_active:
            return # Wait for player to press start

        # The Maw (Wall of Death)
        self.maw_x += MAW_SPEED
        if self.player_sprite.left < self.maw_x and not self.game_over:
            self.trigger_death()

        if self.game_over:
            self.particle_list.update()
            return # Stop all updates if game is over
        if self.won:
            return

        if self.winning_sequence:
            self.win_delay += delta_time
            # Sinking logic
            self.player_sprite.center_y -= 0.6
            self.player_sprite.alpha = max(0, self.player_sprite.alpha - 3)
            if self.player_sprite.alpha == 0 and self.win_delay > 1.8:
                self.won = True
                self.finish_time = self.timer
            self.particle_list.update()
            return

        self.timer += delta_time # Update timer

        if self.physics_engine:
            # Subtle power multiplier (0.8 to 1.1 based on energy)
            energy_mult = 0.8 + (0.3 * self.energy)
            
            # Stun and apply movement
            if self.stun_timer > 0:
                self.stun_timer -= delta_time
            elif self.left_pressed and not self.right_pressed:
                self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED * energy_mult
            elif self.right_pressed and not self.left_pressed:
                self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED * energy_mult
            else:
                self.player_sprite.change_x = 0

            self.physics_engine.update()
            
            # Landing Check for feedback
            engine = self.physics_engine 
            on_ground = engine.can_jump()
            if on_ground and not self.was_on_ground:
                # Just landed!
                self.spawn_dust(self.player_sprite.center_x, self.player_sprite.bottom, count=25)
                self.player_sprite.scale_y = CHARACTER_SCALING * 0.7
                self.player_sprite.scale_x = CHARACTER_SCALING * 1.3
            self.was_on_ground = on_ground
        
        self.player_sprite.update_animation(delta_time)
        self.particle_list.update()
        
        # Slime Knockback Logic
        slime_hits = arcade.check_for_collision_with_list(self.player_sprite, self.leech_list)
        for slime in slime_hits:
            # Heavy knockback away from slime
            diff_x = self.player_sprite.center_x - slime.center_x
            knock_dir = 1 if diff_x > 0 else -1
            # We apply strong upward and horizontal velocity without forcing coordinates, to prevent physics breakage
            self.player_sprite.change_x = knock_dir * 35 # High horizontal force
            self.player_sprite.change_y = 18 # Strong upward pop

            self.stun_timer = 0.4
            
            # Change slime visual
            slime.texture = slime.walk_texture
            
        # Standard updates for non-attached slimes
        self.leech_list.update()
        
        # Energy Decay
        decay_rate = ENERGY_DECAY
        self.energy -= decay_rate * delta_time
        if self.energy < 0:
            self.energy = 0
            
        # Smooth the UI bar
        self.display_energy = arcade.math.lerp(self.display_energy, self.energy, 0.1)
        
        # Camera Smoothing (Lerp)
        tx = self.player_sprite.center_x
        ty = self.player_sprite.center_y
        
        cx, cy = self.camera.position
        new_x = arcade.math.lerp(cx, tx, CAMERA_SPEED)
        new_y = arcade.math.lerp(cy, ty, CAMERA_SPEED)
        
        # Constraints
        if new_x < self.width/2: new_x = self.width/2
        
        self.camera.position = (new_x, new_y)
        
        # Parallax displacement logic (Centered around start position)
        px = SCREEN_WIDTH/2 + (new_x - SCREEN_WIDTH/2) * 0.1
        py = SCREEN_HEIGHT/2 + (new_y - SCREEN_HEIGHT/2) * 0.05
        self.bg_camera_far.position = (px, py)
        
        # Check collisions
        hits = arcade.check_for_collision_with_list(self.player_sprite, self.item_list)
        for h in hits:
            if h.properties.get("is_water"):
                if not self.winning_sequence:
                    self.winning_sequence = True
                    # MassiveSplash: 10x particles
                    self.spawn_dust(self.player_sprite.center_x, self.player_sprite.bottom, dy=12, count=250, color=arcade.color.CYAN)
                    self.spawn_dust(self.player_sprite.center_x, self.player_sprite.bottom, dy=18, count=250, color=arcade.color.AZURE)
            elif h.properties.get("is_goal"):
                # This is now just a visual flag, not the win condition
                pass
            else:
                h.remove_from_sprite_lists()
                self.energy = 1.0 # Refill bar

        # Update Distance
        self.max_dist = max(self.max_dist, int(self.player_sprite.center_x))

        # Void check
        if (self.player_sprite.center_y < DEATH_HEIGHT or self.energy <= 0) and not self.game_over:
            self.trigger_death()

    def on_key_press(self, key, modifiers):
        if self.menu_active:
            if key == arcade.key.SPACE or key == arcade.key.ENTER:
                self.menu_active = False
                self.setup() # starts game properly
            return

        if self.game_over or self.won:
            if key == arcade.key.R or key == arcade.key.SPACE:
                self.setup()
            return # Ignore input if game is over

        if key in [arcade.key.UP, arcade.key.W, arcade.key.SPACE]:
            if self.physics_engine and self.physics_engine.can_jump():
                # Subtle jump multiplier (0.95x to 1.10x)
                jump_mult = 0.95 + (0.15 * self.energy)
                self.player_sprite.change_y = PLAYER_JUMP_SPEED * jump_mult
                
                self.player_sprite.scale_y = CHARACTER_SCALING * 1.5
                self.player_sprite.scale_x = CHARACTER_SCALING * 0.7
                self.spawn_dust(self.player_sprite.center_x, self.player_sprite.bottom, count=35)
        elif key in [arcade.key.LEFT, arcade.key.A]:
            self.left_pressed = True
        elif key in [arcade.key.RIGHT, arcade.key.D]:
            self.right_pressed = True

    def on_key_release(self, key, modifiers):
        if self.game_over:
            return # Ignore input if game is over

        if key in [arcade.key.LEFT, arcade.key.A]:
            self.left_pressed = False
        elif key in [arcade.key.RIGHT, arcade.key.D]:
            self.right_pressed = False

    def on_mouse_press(self, x, y, button, modifiers):
        if self.menu_active:
             bx, by = SCREEN_WIDTH/2, SCREEN_HEIGHT/2 - 50
             if abs(x - bx) < 100 and abs(y - by) < 30:
                 self.menu_active = False
                 self.setup()
             return

        if self.game_over:
           # Check if button clicked (centered at SCREEN_WIDTH/2, SCREEN_HEIGHT/2 - 50, size 200x60)
            bx, by = SCREEN_WIDTH/2, SCREEN_HEIGHT/2 - 50
            if abs(x - bx) < 100 and abs(y - by) < 30:
                self.setup()

    def on_mouse_motion(self, x, y, dx, dy):
        self.mouse_x, self.mouse_y = x, y

def main():
    game = MyGame()
    arcade.set_background_color((10, 5, 20))
    game.setup()
    arcade.run()

if __name__ == "__main__":
    main()
