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)

# Define grid size
GRID_WIDTH = 21
GRID_HEIGHT = 17

# 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)

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

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

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

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

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

    # Check if the line intersects with an enemy or a barrier
    collided_enemies = pygame.sprite.spritecollide(line_sprite, enemies, True)
    collided_barriers = pygame.sprite.spritecollide(line_sprite, barriers, True)

    # Draw the rotated line on the screen
    screen.blit(rotated_line_surf, line_sprite.rect)
    
def bouncy_shoes(player_rect):
    # Set a global variable to indicate that the player has bouncy shoes
    global bouncy
    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 = pygame.image.load('go_kart.png')
    # Draw the go kart over the player
    screen.blit(go_kart, player_rect)
    # Check if the player collides with an enemy or a barrier
    if pygame.sprite.spritecollideany(player, enemies) or pygame.sprite.spritecollideany(player, barriers):
        # If the player collides with an enemy or a barrier, run over it and destroy it
        pygame.sprite.spritecollide(player, enemies, True)
        pygame.sprite.spritecollide(player, 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)
    # Draw a line from the player to the mouse
    pygame.draw.line(screen, BLUE, (player_x, player_y), (mouse_x, mouse_y), 5)
    # Check if the line intersects with an enemy or a platform
    if pygame.sprite.spritecollideany(line, enemies) or pygame.sprite.spritecollideany(line, platforms):
        # If the line intersects with an enemy or a platform, freeze it
        pygame.sprite.spritecollide(line, enemies, False, freeze)
        pygame.sprite.spritecollide(line, platforms, False, freeze)

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):
    # Set a global variable to indicate that the player has used the tsunami maker
    global tsunami
    tsunami = True
    # Load the image for the wave
    wave = pygame.image.load('wave.png')
    # Create a rect for the wave
    wave_rect = wave.get_rect()
    # Set the wave's initial position at the left edge of the screen
    wave_rect.left = 0
    # Set the wave's speed
    wave_speed = 10
    # Draw the wave on the screen
    screen.blit(wave, wave_rect)
    # Move the wave to the right
    wave_rect.x += wave_speed
    # Check if the wave collides with an enemy or a gap
    if pygame.sprite.spritecollideany(wave, enemies) or pygame.sprite.spritecollideany(wave, gaps):
        # If the wave collides with an enemy or a gap, wash it away
        pygame.sprite.spritecollide(wave, enemies, True)
        pygame.sprite.spritecollide(wave, gaps, True, fill)

def fill(sprite):
    # Change the sprite's image to a water version
    sprite.image = pygame.image.load(sprite.name + '_water.png')
    # Set the sprite's filled attribute to True
    sprite.filled = True

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
    # 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:
    # 1. Handle events
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif 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
            elif event.key == ord(button_assignments[0].upper()):  # Use the first selected tool
                try:
                    button_assigned_functions[0](player_sprite.rect)  # Pass player_sprite.rect
                except Exception as e:
                    print(f"Error calling tool function: {e}")
            elif event.key == ord(button_assignments[1].upper()):  # Use the second selected tool
                try:
                    button_assigned_functions[1](player_sprite.rect)  # Pass player_sprite.rect
                except Exception as e:
                    print(f"Error calling tool function: {e}")
            elif event.key == ord(button_assignments[2].upper()):  # Use the third selected tool
                try:
                    button_assigned_functions[2](player_sprite.rect)  # Pass player_sprite.rect
                except Exception as e:
                    print(f"Error calling tool function: {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:
        # Draw the invisibility cloak image over the player
        screen.blit(invisibility_cloak_image, player_sprite.rect)

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

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

if __name__ == "__main__":
    main() how to stop game from closing  because it shows error message but i cant see it cause game closes