import pygame
import random
import math

# Initialize pygame and create a screen object
pygame.init()
screen = pygame.display.set_mode((1000, 800))

# Define colors
BLACK = (0, 0, 0)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
BLUE = (0, 0, 255)

# Define grid size
GRID_WIDTH = 21
GRID_HEIGHT = 17

# Initialize the invisible variable
invisible = False

# Define initial floor position
FLOOR_START_ROW = GRID_HEIGHT - 2
FLOOR_END_ROW = GRID_HEIGHT - 1

pygame.display.set_caption('100% no luck')

# Load the images for the background, the player, and the boss
# Ensure that these images are present in your project directory
try:
    background = pygame.image.load('level_1_background.jpeg')
    player_image = pygame.image.load('player1_back.png')
    invisibility_cloak_image = pygame.image.load('invisibility_cloak.png')
    grappling_hook_image = pygame.image.load('grappling_hook.png')
    katana_image = pygame.image.load('katana.png')
    freezing_ray_image = pygame.image.load('freezing_ray.png')
    speed_pill_image = pygame.image.load('speed_pill.jpeg')
    bouncy_shoes_image = pygame.image.load('bouncy_shoes.png')
    key_image = pygame.image.load('key.png')
    tsunami_maker_image = pygame.image.load('tsunami_maker.png')
    laser_beam_image = pygame.image.load('laser_beam.png')
    go_kart_image = pygame.image.load('go_kart.jpeg')
    revival_kit_image = pygame.image.load('revival_kit.jpeg')
except pygame.error as e:
    print("Cannot load images:", e)
    raise SystemExit

# Define the colors for the text
WHITE = (255, 255, 255)

# Define the font for the text
font = pygame.font.SysFont('Arial', 32)

# Define the text for the intro and the tools
intro_text = "Welcome to 100% no luck, a game where you have to use smart logic and some luck to beat the dungeon. You will spawn with 3 tools out of a possible 15 tools randomized. Each tool has a different effect and can help you or hinder you depending on the situation. You have to think carefully about how to use your tools and when to use them. The dungeon has many obstacles and enemies that you have to overcome. The goal is to reach the end of the dungeon and defeat the boss. Good luck!"

tools_text = ["Invisibility cloak: Makes you invisible for 10 seconds. Useful for sneaking past enemies or traps.",
"Grappling hook: Allows you to swing across gaps or pull objects towards you.",
"Katana: A sharp sword that can cut through anything. Useful for fighting enemies or breaking barriers.",
"Freeze ray: A device that can freeze anything in its path. Useful for stopping enemies or creating platforms.",
"Bouncy shoes: Shoes that make you jump higher and farther. Useful for reaching high places or avoiding obstacles.",
"Key: A key that can unlock any door or chest. Useful for accessing hidden areas or treasures.",
"Go kart: A vehicle that can move fast and run over anything. Useful for escaping enemies or crossing large distances.",
"Tsunami maker: A device that can create a huge wave of water. Useful for washing away enemies or filling gaps.",
"Laser beam: A device that can shoot a powerful laser. Useful for destroying enemies or barriers.",
"Speed pill: A pill that can make you move faster for 10 seconds. Useful for dodging enemies or traps.",
"Revival kit: A kit that can revive you once if you die. Useful for surviving fatal situations."]



def show_intro():
    intro_text = "Welcome to 100% no luck, a game where you have to use smart logic and some luck to beat the dungeon. You will spawn with 3 tools out of a possible 15 tools randomized. Each tool has a different effect and can help you or hinder you depending on the situation. You have to think carefully about how to use your tools and when to use them. The dungeon has many obstacles and enemies that you have to overcome. The goal is to reach the end of the dungeon and defeat the boss. Good luck!"

    # Wrap the intro text
    intro_lines = []
    max_width = 950  # Adjust as needed
    words = intro_text.split()
    current_line = words[0]
    for word in words[1:]:
        if font.size(current_line + ' ' + word)[0] < max_width:
            current_line += ' ' + word
        else:
            intro_lines.append(current_line)
            current_line = word
    intro_lines.append(current_line)

    current_line = 0
    intro_complete = False
    start_time = pygame.time.get_ticks()

    while not intro_complete:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        screen.fill((0, 0, 0))
        intro_surface = font.render(intro_lines[current_line], True, WHITE)
        intro_rect = intro_surface.get_rect(center=(screen.get_width() // 2, screen.get_height() // 2))
        screen.blit(intro_surface, intro_rect)

        if pygame.time.get_ticks() - start_time > 2980:
            current_line += 1
            start_time = pygame.time.get_ticks()

        if current_line == len(intro_lines):
            intro_complete = True

        pygame.display.flip()
        pygame.time.Clock().tick(60)

    screen.fill((0, 0, 0))
    pygame.display.flip()

    tools_title_surface = font.render("Selected Tools:", True, WHITE)
    tools_title_rect = tools_title_surface.get_rect(center=(screen.get_width() // 2, 100))
    screen.blit(tools_title_surface, tools_title_rect)
    pygame.display.flip()

    pygame.time.delay(1000)

    available_buttons = ['v', 'b', 'n', 'x', 'y', 'z']  # Add more buttons if needed
    selected_tools = random.sample(tools_text, 3)

    # Assign buttons to the selected tools
    button_assignments = available_buttons[:3]  # Take the first three buttons
    button_assigned_functions = [tool_functions[tools_text.index(tool)] for tool in selected_tools]

    # Wrap the tool descriptions and display them with their assigned buttons
    wrapped_tools = []
    for i, tool_desc in enumerate(selected_tools):
        tool_lines = []
        current_line = f"{button_assignments[i]}: {tool_desc.split()[0]}"
        for word in tool_desc.split()[1:]:
            if font.size(current_line + ' ' + word)[0] < max_width:
                current_line += ' ' + word
            else:
                tool_lines.append(current_line)
                current_line = word
        tool_lines.append(current_line)
        wrapped_tools.append(tool_lines)

    selected_tools_surface = [font.render(line, True, WHITE) for tool_lines in wrapped_tools for line in tool_lines]

    for i, tool_surface in enumerate(selected_tools_surface):
        tool_rect = tool_surface.get_rect(center=(screen.get_width() // 2, 200 + i * 50))
        screen.blit(tool_surface, tool_rect)
        pygame.display.flip()

    start_time = pygame.time.get_ticks()
    while pygame.time.get_ticks() - start_time < 2000:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

    screen.fill((0, 0, 0))
    pygame.display.flip()
   
    return button_assignments, button_assigned_functions

# Define the logic for each tool as a function
def invisibility_cloak(player_rect):
    # Set a global variable to indicate that the player is invisible
    global invisible
    invisible = True
    # Set a timer to make the player visible again after 10 seconds
    pygame.time.set_timer(pygame.USEREVENT, 10000)

    # Create a Rect for the invisibility cloak sprite
    cloak_rect = player_rect.copy()
    cloak_rect.inflate_ip(50, 50)

    # Load the invisibility cloak image
    try:
        cloak_surf = pygame.image.load('invisibility_cloak.png')
    except pygame.error as e:
        print(f"Cannot load invisibility cloak image: {e}")
        return

    while invisible:
        # Draw the invisibility cloak sprite over the player
        screen.blit(cloak_surf, cloak_rect)

        # Update the cloak position based on the player's position
        cloak_rect.center = player_rect.center

        # Check for events
        for event in pygame.event.get():
            if event.type == pygame.USEREVENT:
                # Stop the invisibility cloak effect
                invisible = False

        # Update the display
        pygame.display.flip()

        # Limit the frame rate
        pygame.time.Clock().tick(60)

    # Reset the invisible state
    invisible = False

def grappling_hook(player_rect):
    # Get the mouse position and the player position
    mouse_x, mouse_y = pygame.mouse.get_pos()
    player_x, player_y = player_rect.center
    # Calculate the distance and the angle between the mouse and the player
    distance = ((mouse_x - player_x) ** 2 + (mouse_y - player_y) ** 2) ** 0.5
    angle = math.atan2(mouse_y - player_y, mouse_x - player_x)
    # Draw a line from the player to the mouse
    pygame.draw.line(screen, BLACK, (player_x, player_y), (mouse_x, mouse_y), 5)
    # Check if the mouse is over a gap or an object
    if mouse_x > 600 and mouse_x < 700 and mouse_y > 300 and mouse_y < 400:
        # If the mouse is over a gap, move the player towards the mouse
        player_rect.centerx += math.cos(angle) * 10
        player_rect.centery += math.sin(angle) * 10
    elif mouse_x > 700 and mouse_x < 800 and mouse_y > 200 and mouse_y < 300:
        # If the mouse is over an object, move the object towards the player
        object_rect.centerx -= math.cos(angle) * 10
        object_rect.centery -= math.sin(angle) * 10

def katana(player_rect):
    # Get the mouse position and the player position
    mouse_x, mouse_y = pygame.mouse.get_pos()
    player_x, player_y = player_rect.center

    # Randomly choose between the two attacks
    attack_type = random.randint(1, 2)

    # Load the katana image
    try:
        katana_surf = pygame.image.load('katana.png')
    except pygame.error as e:
        print(f"Cannot load katana image: {e}")
        return

    # Create a temporary sprite for the katana
    katana_sprite = pygame.sprite.Sprite()
    katana_sprite.image = katana_surf
    katana_sprite.rect = katana_sprite.image.get_rect()
    katana_sprite.rect.center = player_rect.center

    if attack_type == 1:
        # Attack 1: Player moves/slashes in a random direction
        angle = random.uniform(0, 2 * math.pi)  # Random angle
        slash_distance = 198  # Distance to move/slash
        new_x = player_x + math.cos(angle) * slash_distance
        new_y = player_y + math.sin(angle) * slash_distance
        player_rect.center = (new_x, new_y)

        # Draw the katana image on the player
        katana_sprite.rect.center = player_rect.center
        screen.blit(katana_sprite.image, katana_sprite.rect)

    else:
        # Attack 2: Throw the katana towards the mouse position
        angle = math.atan2(mouse_y - player_y, mouse_x - player_x)
        katana_sprite.image = pygame.transform.rotate(katana_sprite.image, math.degrees(angle))
        katana_sprite.rect = katana_sprite.image.get_rect()
        katana_sprite.rect.center = (player_x, player_y)
        screen.blit(katana_sprite.image, katana_sprite.rect)

        # Move the katana towards the mouse position
        dx = mouse_x - player_x
        dy = mouse_y - player_y
        distance = (dx ** 2 + dy ** 2) ** 0.5
        katana_speed = 10
        katana_sprite.rect.x += dx / distance * katana_speed
        katana_sprite.rect.y += dy / distance * katana_speed

        # Check if the katana has reached the mouse position
        if katana_sprite.rect.collidepoint(mouse_x, mouse_y):
            # Reset the katana position
            katana_sprite.rect.center = player_rect.center
    
def bouncy_shoes(player_rect):
    # Set a global variable to indicate that the player has bouncy shoes
    global bouncy, player_jump_vel
    bouncy = True
    # Increase the player's jump velocity
    player_jump_vel = 20

def key(player_rect):
    # Get the mouse position and the player position
    mouse_x, mouse_y = pygame.mouse.get_pos()
    player_x, player_y = player_rect.center
    # Calculate the distance between the mouse and the player
    distance = ((mouse_x - player_x) ** 2 + (mouse_y - player_y) ** 2) ** 0.5
    # Check if the mouse is over a door or a chest
    if pygame.sprite.spritecollideany(mouse, doors) or pygame.sprite.spritecollideany(mouse, chests):
        # If the mouse is over a door or a chest, unlock it and open it
        pygame.sprite.spritecollide(mouse, doors, True)
        pygame.sprite.spritecollide(mouse, chests, True)
        # If the chest contains a tool, add it to the player's inventory
        if chest.tool:
            inventory.append(chest.tool)

def go_kart(player_rect):
    # Set a global variable to indicate that the player is in a go kart
    global go_kart
    go_kart = True
    # Increase the player's speed and size
    player_speed = 15
    player_rect.inflate_ip(50, 50)
    # Load the image for the go kart
    go_kart_image = pygame.image.load('go_kart.jpeg')
    # Draw the go kart over the player
    screen.blit(go_kart_image, player_rect)
    # Check if the player collides with an enemy or a barrier
    collided_enemies = pygame.sprite.spritecollide(player_sprite, enemies, True)
    collided_barriers = pygame.sprite.spritecollide(player_sprite, barriers, True)


def freeze_ray(player_rect):
    # Get the mouse position and the player position
    mouse_x, mouse_y = pygame.mouse.get_pos()
    player_x, player_y = player_rect.center

    # Calculate the angle between the mouse and the player
    angle = math.atan2(mouse_y - player_y, mouse_x - player_x)

    # Create a line surface from the player to the mouse
    line_length = math.hypot(mouse_x - player_x, mouse_y - player_y)
    line = pygame.Surface((line_length, 5), pygame.SRCALPHA)
    line.fill(BLUE)
    line_rect = line.get_rect()
    line_rect.center = player_rect.center

    # Rotate the line surface
    rotated_line = pygame.transform.rotate(line, math.degrees(angle))

    # Draw the rotated line on the screen
    screen.blit(rotated_line, (player_x, player_y))

    # Create a temporary sprite for the line
    line_sprite = pygame.sprite.Sprite()
    line_sprite.image = rotated_line
    line_sprite.rect = rotated_line.get_rect(center=line_rect.center)

    # Check if the line intersects with an enemy or a platform
    enemies_hit = pygame.sprite.spritecollide(line_sprite, enemies, False)
    platforms_hit = pygame.sprite.spritecollide(line_sprite, platforms, False)

    # Freeze the enemies and platforms that were hit
    for enemy in enemies_hit:
        freeze(enemy)
    for platform in platforms_hit:
        freeze(platform)

def freeze(sprite):
    # Change the sprite's image to a frozen version
    sprite.image = pygame.image.load(sprite.name + '_frozen.png')
    # Set the sprite's frozen attribute to True
    sprite.frozen = True

def tsunami_maker(player_rect):
    global tsunami
    tsunami = True
    wave_surf = pygame.image.load('tsunami_maker.png')
    # Resize the wave_surf to make it taller
    wave_surf = pygame.transform.scale(wave_surf, (wave_surf.get_width(), wave_surf.get_height() * 3))
    wave_sprite = pygame.sprite.Sprite()
    wave_sprite.image = wave_surf
    wave_sprite.rect = wave_sprite.image.get_rect()
    wave_sprite.rect.bottomleft = (0, screen.get_height())
    wave_speed = 10

    while tsunami:
        screen.blit(wave_sprite.image, wave_sprite.rect)
        wave_sprite.rect.x += wave_speed

        if wave_sprite.rect.right >= screen.get_width():
            tsunami = False

        pygame.display.flip()


def laser_beam(player_rect):
    # Get the mouse position and the player position
    mouse_x, mouse_y = pygame.mouse.get_pos()
    player_x, player_y = player_rect.center
    # Calculate the angle between the mouse and the player
    angle = math.atan2(mouse_y - player_y, mouse_x - player_x)
    # Draw a line from the player to the mouse
    pygame.draw.line(screen, YELLOW, (player_x, player_y), (mouse_x, mouse_y), 5)
    # Check if the line intersects with an enemy or a barrier
    if pygame.sprite.spritecollideany(line, enemies) or pygame.sprite.spritecollideany(line, barriers):
        # If the line intersects with an enemy or a barrier, destroy it
        pygame.sprite.spritecollide(line, enemies, True)
        pygame.sprite.spritecollide(line, barriers, True)

def speed_pill(player_rect):
    # Set a global variable to indicate that the player has used the speed pill
    global speed
    speed = True
    # Set a timer to make the player normal again after 10 seconds
    pygame.time.set_timer(pygame.USEREVENT + 1, 10000)
    # Increase the player's speed
    player_speed = 10

def revival_kit(player_rect):
    # Set a global variable to indicate that the player has a revival kit
    global revival
    revival = True
    global player_health
    # Check if the player's health is zero
    if player_health == 0:
        # If the player's health is zero, revive the player
        player_health = 100
        # Set the revival variable to False
        revival = False

tool_functions = [invisibility_cloak, grappling_hook, katana, freeze_ray, bouncy_shoes, key,
                  go_kart, tsunami_maker, laser_beam, speed_pill, revival_kit]

def is_on_floor_or_stairs(player_rect):
    """
    Check if the player is standing on a floor or stairs cell.
    Returns True if standing on floor or stairs, False otherwise.
    """
    cell_width = screen.get_width() // GRID_WIDTH
    cell_height = screen.get_height() // GRID_HEIGHT

    # Check if the player is on the initial floor rows
    if FLOOR_START_ROW <= player_rect.bottom // cell_height <= FLOOR_END_ROW:
        for x in range(player_rect.left // cell_width, (player_rect.right + cell_width - 1) // cell_width):
            if 0 <= x < GRID_WIDTH:
                return True

    

    return False

def draw_doors():
    # Define the door's color
    DOOR_COLOR = (139, 69, 19)  # Saddle brown color

    # Calculate the cell size based on the screen dimensions
    cell_width = screen.get_width() // GRID_WIDTH
    cell_height = screen.get_height() // GRID_HEIGHT

    # Draw door 1
    door1_start_row = GRID_HEIGHT - 10
    door1_end_row = GRID_HEIGHT - 13
    door1_start_col = 10
    door1_end_col = 11

    for row in range(door1_start_row, door1_end_row - 1, -1):
        for col in range(door1_start_col, door1_end_col + 1):
            rect = pygame.Rect(col * cell_width, row * cell_height, cell_width, cell_height)
            pygame.draw.rect(screen, DOOR_COLOR, rect)

    # Draw door 2
    door2_start_row = GRID_HEIGHT - 3
    door2_end_row = GRID_HEIGHT - 4
    door2_start_col = 16
    door2_end_col = 17

    for row in range(door2_start_row, door2_end_row - 1, -1):
        for col in range(door2_start_col, door2_end_col + 1):
            rect = pygame.Rect(col * cell_width, row * cell_height, cell_width, cell_height)
            pygame.draw.rect(screen, DOOR_COLOR, rect)

def draw_floor():
    # Define the floor's color
    FLOOR_COLOR = (128, 64, 0)  # Brown color
    STAIRS_COLOR = (128, 128, 128)  # Gray color

    # Calculate the cell size based on the screen dimensions
    cell_width = screen.get_width() // GRID_WIDTH
    cell_height = screen.get_height() // GRID_HEIGHT

    # Draw the floor cells
    for col in range(GRID_WIDTH):
        for row in range(FLOOR_START_ROW, FLOOR_END_ROW + 1):
            rect = pygame.Rect(col * cell_width, row * cell_height, cell_width, cell_height)
            pygame.draw.rect(screen, FLOOR_COLOR, rect)

    

enemies = pygame.sprite.Group()
barriers = pygame.sprite.Group()
platforms = pygame.sprite.Group()
doors = pygame.sprite.Group()
chests = pygame.sprite.Group()
gaps = pygame.sprite.Group()
tools = pygame.sprite.Group()

# Create some sprites and groups for the player
player_sprite = pygame.sprite.Sprite()
player_sprite.image = player_image
player_sprite.rect = player_sprite.image.get_rect()
player_sprite.rect.center = (100, 300)

# Create sprite groups
enemies = pygame.sprite.Group()
tools = pygame.sprite.Group()  # Assuming tools are also Sprites

# Gravity constant
GRAVITY = 0.5

# Player properties
player_x = 100
player_y = 300
player_speed = 5
player_jump_vel = 10
player_vert_vel = 0  # Initial vertical velocity
player_health = 100
inventory = []

# Main game loop control
running = True
clock = pygame.time.Clock()

# Call show_intro() and get the button_assigned_functions list
button_assignments, button_assigned_functions = show_intro()


# Initialize player jumping state
player_is_jumping = False

# Create a rect for the player sprite
player_rect = player_sprite.rect

# In the main game loop
while running:
    # ...
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        
        # Process KEYDOWN events individually
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE and not player_is_jumping:
                # Start jumping
                player_is_jumping = True
                player_vert_vel = -player_jump_vel
            
            if event.key == ord('v'):  # Pressed V
                try:
                    button_assigned_functions[0](player_sprite.rect)  # Pass player_sprite.rect
                except Exception as e:
                    print(f"Error calling tool function 1: {e}")
            elif event.key == ord('b'):  # Pressed B
                try:
                    button_assigned_functions[1](player_sprite.rect)  # Pass player_sprite.rect
                except Exception as e:
                    print(f"Error calling tool function 2: {e}")
            elif event.key == ord('n'):  # Pressed N
                try:
                    button_assigned_functions[2](player_sprite.rect)  # Pass player_sprite.rect
                except Exception as e:
                    print(f"Error calling tool function 3: {e}")
    # 2. Update game state
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        player_sprite.rect.x -= player_speed
    if keys[pygame.K_RIGHT]:
        player_sprite.rect.x += player_speed
    if keys[pygame.K_UP]:
        player_sprite.rect.y -= player_speed
    if keys[pygame.K_DOWN]:
        player_sprite.rect.y += player_speed

    # Update player's vertical velocity and position if jumping
    if player_is_jumping:
        player_vert_vel += GRAVITY
        player_sprite.rect.y += player_vert_vel

        # Check if the player has landed on floor or stairs
        if is_on_floor_or_stairs(player_sprite.rect):
            player_is_jumping = False
            player_vert_vel = 0
    else:
        # Apply gravity only if not on floor or stairs
        if not is_on_floor_or_stairs(player_sprite.rect):
            player_vert_vel += GRAVITY
        player_sprite.rect.y += player_vert_vel

    # Reset vertical velocity if on floor or stairs
    if is_on_floor_or_stairs(player_sprite.rect):
        player_vert_vel = 0

    # Prevent the player from moving off screen
    player_sprite.rect.clamp_ip(screen.get_rect())

    # 3. Draw everything
    screen.blit(background, (0, 0))
    draw_floor()
    draw_doors()
    tools.draw(screen)
    enemies.draw(screen)
    screen.blit(player_sprite.image, player_sprite.rect)  # Draw player

    # Check if the player is invisible
    if invisible:
        # Get the updated player position
        player_x, player_y = player_sprite.rect.topleft
        # Draw the invisibility cloak image over the player
        screen.blit(invisibility_cloak_image, (player_x, player_y))

    # After drawing everything, update/flip the display
    pygame.display.flip()

    # 4. Cap the frame rate
    clock.tick(60)

if __name__ == "__main__":
    main() now code to beat the level the player has to reach enter either door, to enter door 1 player just has to reach it because its open but for door 2 its locked so player needs to have the key.