import pyxel
import random

game_state = "intro"
message_timer = 0
current_message = ""
FOV_RADIUS = 6
intro_scroll_y = 120
intro_speed = 0.3
intro_text = [

"BIOHAZARD INCIDENT",
"",
"You wake up in a quarantined zone.",
"",
"The infection is spreading...",
"",
"Find the cure components.",
"",
"Press SPACE to begin"

]
def update_intro():

    global game_state, intro_scroll_y

    intro_scroll_y -= intro_speed

    # start game when SPACE pressed OR scroll finishes
    if pyxel.btnp(pyxel.KEY_SPACE) or intro_scroll_y < -50:
        game_state = "play"
def draw_intro():

    pyxel.cls(0)

    y = 30

    for line in intro_text:

        pyxel.text(40, y, line, 7)
        y += 10

TILE_SIZE = 8
WIDTH = 160
HEIGHT = 120
camera_x = 0
camera_y = 0
area = 0
# -------------------------
# SOUNDS
# -------------------------

# shoot sound
pyxel.sound(0).set("c3", "p", "7", "n", 10)

# hit enemy
pyxel.sound(1).set("e3", "p", "7", "n", 10)

# player hit
pyxel.sound(2).set("a2", "p", "7", "n", 15)

# pickup (key / ammo / component)
pyxel.sound(3).set("c4e4g4", "p", "7", "n", 20)

# boss hit
pyxel.sound(4).set("f2", "p", "7", "n", 20)
# -------------------------
# MAPS
# -------------------------
def is_visible(tile_x, tile_y):
    dx = tile_x - int(player_x // TILE_SIZE)
    dy = tile_y - int(player_y // TILE_SIZE)
    return (dx * dx + dy * dy) <= (FOV_RADIUS * FOV_RADIUS)
    




MAP_OUTSIDE = [
"111111111111111111111111111111111111",
"100000000000000100000000000000000011",
"100000001100000000000011000000010011",
"100110001100000000110011000000000011",
"100110000000000000110000000110000011",
"100000001100000000000000000110000011",
"100000001100000010000000000000000011",
"100000000000000000000011000000000031",
"100011000000110000000011000000000011",
"100011000000110000000000000000100011",
"100000000000000000011000000000000011",
"100000011000000000011000000100000011",
"100000011000000000000000000000000011",
"100000000000000000000000000000000011",
"111111111111111111111111111111111111",
]

MAP_STORAGE =[

"111111111111111111111111111111111111",
"100001000000100000000001000000000011",
"100001000000100000000000000000100011",
"100000000011111121111111111111100011",
"100000000010000000001000000000000011",
"100001111110000000001000000000000011",
"100000000011110000001111111110000011",
"100000000010000000001000000010000031",
"100001000000000000000000000020000011",
"100001000000000000100000000010000011",
"100001000001000000100000000011121111",
"111111001111111111111111110010000011",
"100000000010000000000000010000000011",
"100000000000000000000000010000000011",
"111111111111111111111111111111111111",
]

MAP_LAB = [

"111111111111111111111111111111111111",
"100000000000000000000000000000000011",
"111111110011111100111111001111111111",
"100000000000000000000000000000000011",
"100000000000000000000000000000000011",
"111111111100111100111111001111111111",
"100000000000000000000000000000000011",
"100000000000000000000000000000000021",
"111111100111111100111111001111111111",
"100000000000000000000000000000000011",
"111001111111111100111111001111111111",
"100000000000000000000000000000000011",
"100000000000000000000000000000000011",
"111111111111111100111111111111111111",
"111111111111111111111111111111111111",
]

MAP_ESCAPE = [

"111111111111111111111111111111111111",
"100000000010000000000000000000100001",
"100000111111111111111100000000000001",
"100000000000000001000000000000000001",
"100000000000000111111111100000100001",
"100000000000000000000000000000100001",
"100000111111111111000000001111111011",
"100000010000000000000000000001000001",
"100001111100011111111111000000000001",
"100000000100000000010000000001000001",
"100011111110011111111001111111111011",
"100000000100000000000000000100000001",
"100000000111011111111111011110000001",
"100000000000010000000000000000000001",
"111111111111111111111111111111111111",
]



MAPS = [
MAP_OUTSIDE,
MAP_STORAGE,
MAP_LAB,
MAP_ESCAPE
]

map_data = MAPS[area]

map_memory = []
def reset_memory():
    global map_memory
    map_memory = [
        [False for _ in range(len(row))]
        for row in map_data
    ]
reset_memory()


boss_flash = 0

boss_damage_timer = 0

ammo_x = 0
ammo_y = 0
ammo_spawned = False
# -------------------------
# PLAYER
# -------------------------

player_x = 80
player_y = 60

player_hp = 3
player_armor = 3
player_infection = 0

ammo = 60
flash_timer = 0

components = 0

component_x = 0
component_y = 0

component_spawned = False
# -------------------------
# KEY SYSTEM
# -------------------------

has_key = False
key_x = 0
key_y = 0

def map_has_locked_door():

    for row in map_data:
        if "3" in row:
            
            return True

    return False


def spawn_key():

    global key_x, key_y, has_key

    has_key = False

    if not map_has_locked_door():
        return

    while True:

        x = random.randint(1,18)
        y = random.randint(1,8)

        if map_data[y][x] == "0":
            
            key_x = x * TILE_SIZE
            key_y = y * TILE_SIZE

            return


def check_key():

    global has_key

    if map_has_locked_door() and not has_key:

        if abs(player_x-key_x) < 6 and abs(player_y-key_y) < 6:

            has_key = True
            pyxel.play(0, 3)

def unlock_door():

    global map_data

    if not has_key:
        return

    tile_x = int(player_x // TILE_SIZE)
    tile_y = int(player_y // TILE_SIZE)

    if map_data[tile_y][tile_x] == "3":

        row = list(map_data[tile_y])

        row[tile_x] = "2"

        map_data[tile_y] = "".join(row)

def spawn_ammo():

    global ammo_x, ammo_y, ammo_spawned
    
    x = random.randint(1,18)
    y = random.randint(1,8)

    if map_data[y][x] == "0":

        ammo_x = x * TILE_SIZE
        ammo_y = y * TILE_SIZE

        ammo_spawned = True
        return
def check_ammo():

    global ammo, ammo_spawned

    if not ammo_spawned:
        return

    if abs(player_x-ammo_x) < 6 and abs(player_y-ammo_y) < 6:

        ammo += 5
        pyxel.play(0, 3)
        ammo_spawned = False
def draw_ammo():

    if ammo_spawned:
        pyxel.rect(ammo_x,ammo_y,4,4,12)
# -------------------------
# ENEMIES
# -------------------------

enemy_x = []
enemy_y = []
enemy_hp = []
enemy_type = []

LEVELS = [
{"enemy_count":3,"type":"walker"},
{"enemy_count":4,"type":"walker"},
{"enemy_count":3,"type":"runner"},
{"enemy_count":0,"type":"boss"},
]


def spawn_level():

    enemy_x.clear()
    enemy_y.clear()
    enemy_hp.clear()
    enemy_type.clear()

    level = LEVELS[area]

    map_height = len(map_data)
    map_width = len(map_data[0])

    for i in range(level["enemy_count"]):

        while True:

            x = random.randint(1, map_width - 2)
            y = random.randint(1, map_height - 2)

            if map_data[y][x] == "0":

                enemy_x.append(x * TILE_SIZE)
                enemy_y.append(y * TILE_SIZE)

                if level["type"] == "walker":
                    enemy_hp.append(1)
                    enemy_type.append("walker")
                else:
                    enemy_hp.append(2)
                    enemy_type.append("runner")

                break
#----------------------------------------------
#--------------Components----------------------
#----------------------------------------------
def show_message(text, time=60):

    global current_message, message_timer

    current_message = text
    message_timer = time
def draw_message():

    if message_timer > 0:

        pyxel.rect(20, HEIGHT-30, WIDTH-40, 20, 1)

        pyxel.text(
            30,
            HEIGHT-22,
            current_message,
            7
        )
def spawn_component():

    global component_x, component_y, component_spawned

    component_spawned = False

    map_height = len(map_data)
    map_width = len(map_data[0])

    # safety check
    if map_height < 3 or map_width < 3:
        return

    attempts = 0

    while attempts < 1000:  # prevents infinite loop

        x = random.randint(1, map_width - 2)
        y = random.randint(1, map_height - 2)

        if map_data[y][x] == "0":

            component_x = x * TILE_SIZE
            component_y = y * TILE_SIZE
            component_spawned = True
            return

        attempts += 1
def draw_component():
    if not map_memory[int(component_y//TILE_SIZE)][int(component_x//TILE_SIZE)]:
        return
    if not component_spawned:
        return

    x = component_x - camera_x
    y = component_y - camera_y

    pyxel.rect(x, y, 4, 4, 11)
    pyxel.pset(x+1, y, 7)
    pyxel.pset(x+2, y+3, 7)
    pyxel.pset(x+3, y+1, 7)

def check_component():

    global components, component_spawned

    if not component_spawned:
        return

    if abs(player_x-component_x) < 6 and abs(player_y-component_y) < 6:

        components += 1
        pyxel.play(0, 3)
        component_spawned = False
        show_message("Cure component secured")

# -------------------------
# BOSS
# -------------------------

boss_x = 80
boss_y = 20
boss_hp = 30
boss_active = False

def draw_boss_hp():
    if boss_active and boss_hp > 0:
        # background bar
        pyxel.rect(20, 5, 120, 6, 7)  # gray background
        # foreground bar proportional to HP
        hp_width = int(120 * (boss_hp / 5))  # max HP = 5
        pyxel.rect(20, 5, hp_width, 6, 8)  # red foreground
        pyxel.text(145, 5, "BOSS", 7)
    
# -------------------------
# BULLETS
# -------------------------

bullet_x = []
bullet_y = []
bullet_dx = []
bullet_dy = []

boss_bullet_x = []
boss_bullet_y = []
boss_bullet_dx = []
boss_bullet_dy = []

def shoot():

    global ammo

    if ammo <= 0:
        return

    dx = 0
    dy = 0

    # choose direction
    if pyxel.btn(pyxel.KEY_UP):
        dy = -2

    elif pyxel.btn(pyxel.KEY_DOWN):
        dy = 2

    elif pyxel.btn(pyxel.KEY_LEFT):
        dx = -2

    elif pyxel.btn(pyxel.KEY_RIGHT):
        dx = 2

    else:
        return   # don't shoot if no direction
    ammo -= 1
    pyxel.play(0, 0)
    bullet_x.append(player_x)
    bullet_y.append(player_y)
    bullet_dx.append(dx)
    bullet_dy.append(dy)
   
def boss_shoot():

    dx = player_x - boss_x
    dy = player_y - boss_y

    length = max(1, abs(dx) + abs(dy))

    dx = dx / length
    dy = dy / length

    boss_bullet_x.append(boss_x)
    boss_bullet_y.append(boss_y)

    boss_bullet_dx.append(dx * 1.5)
    boss_bullet_dy.append(dy * 1.5)

def bullets_update():

    for i in reversed(range(len(bullet_x))):

        bullet_x[i] += bullet_dx[i]
        bullet_y[i] += bullet_dy[i]

        # remove if hits wall
        if is_wall(bullet_x[i], bullet_y[i]):

            bullet_x.pop(i)
            bullet_y.pop(i)
            bullet_dx.pop(i)
            bullet_dy.pop(i)
            continue

        # remove if off screen
        if bullet_y[i] < 0:

            bullet_x.pop(i)
            bullet_y.pop(i)
            bullet_dx.pop(i)
            bullet_dy.pop(i)


def bullet_hits():

    global boss_hp, boss_flash

    for b in reversed(range(len(bullet_x))):

        hit = False

        # enemy hits
        for e in range(len(enemy_x)):

            if enemy_hp[e] <= 0:
                continue

            if abs(bullet_x[b] - enemy_x[e]) < 6 and abs(bullet_y[b] - enemy_y[e]) < 6:

                enemy_hp[e] -= 1
                pyxel.play(0, 1)
                hit = True
                break

        # boss hits (FIXED: properly indented INSIDE function)
        if boss_active and not hit:

            if abs(bullet_x[b] - boss_x) < 10 and abs(bullet_y[b] - boss_y) < 10:

                boss_hp -= 1
                boss_flash = 5
                hit = True

        # remove bullet if it hit anything
        if hit:
            bullet_x.pop(b)
            bullet_y.pop(b)
            bullet_dx.pop(b)
            bullet_dy.pop(b)
# -------------------------
# COLLISION
# -------------------------

def is_wall(x,y):

    tile_x = int(x//TILE_SIZE)
    tile_y = int(y//TILE_SIZE)

    tile = map_data[tile_y][tile_x]

    if tile == "1":
        return True

    if tile == "3" and not has_key:
        return True

    return False


# -------------------------
# UPDATE
# -------------------------
def camera_update():

    global camera_x, camera_y

    # center camera on player
    camera_x = player_x - WIDTH // 2
    camera_y = player_y - HEIGHT // 2

    # stop camera leaving map edges
    max_x = len(map_data[0]) * TILE_SIZE - WIDTH
    max_y = len(map_data) * TILE_SIZE - HEIGHT

    camera_x = max(0, min(camera_x, max_x))
    camera_y = max(0, min(camera_y, max_y))
    
def reset_game():

    global player_x, player_y
    global player_hp, player_armor, player_infection
    global ammo, components
    global area, map_data
    global boss_hp, boss_active
    global game_state

    player_x = 80
    player_y = 60

    player_hp = 3
    player_armor = 3
    player_infection = 0

    ammo = 60
    components = 0

    area = 0
    map_data = MAPS[area]

    boss_hp = 30   # ✅ FIXED
    boss_active = False

    spawn_level()
    spawn_key()
    spawn_component()

    game_state = "play"   # ⚠️ make sure this matches your system
    
def player_update():

    global player_x,player_y,player_infection,flash_timer,player_hp

    speed = 1

    if pyxel.frame_count % 120 == 0:
        player_infection += 1

    if pyxel.btnp(pyxel.KEY_SPACE):
        shoot()

    new_x = player_x
    new_y = player_y

    if pyxel.btn(pyxel.KEY_W):
        new_y -= speed

    if pyxel.btn(pyxel.KEY_S):
        new_y += speed

    if pyxel.btn(pyxel.KEY_A):
        new_x -= speed

    if pyxel.btn(pyxel.KEY_D):
        new_x += speed

    if not is_wall(new_x,player_y):
        player_x = new_x

    if not is_wall(player_x,new_y):
        player_y = new_y
    player_tile_x = int(player_x // TILE_SIZE)
    player_tile_y = int(player_y // TILE_SIZE)

    for y in range(player_tile_y - FOV_RADIUS, player_tile_y + FOV_RADIUS + 1):
        for x in range(player_tile_x - FOV_RADIUS, player_tile_x + FOV_RADIUS + 1):

            if 0 <= y < len(map_memory) and 0 <= x < len(map_memory[0]):
    
                dx = x - player_tile_x
                dy = y - player_tile_y
    
                if dx * dx + dy * dy <= FOV_RADIUS * FOV_RADIUS:
                    map_memory[y][x] = True
    tile_y = int(player_y // TILE_SIZE)
    tile_x = int(player_x // TILE_SIZE)
    if 0 <= tile_y < len(map_memory):
        if 0 <= tile_x < len(map_memory[0]):
            map_memory[tile_y][tile_x] = True
       

def enemies_update():

    global player_hp,player_armor

    for i in range(len(enemy_x)):

        if enemy_hp[i] <= 0:
            continue

        speed = 0.5

        if enemy_type[i] == "runner":
            speed = 1

        new_x = enemy_x[i]
        new_y = enemy_y[i]

        if enemy_x[i] < player_x:
            new_x += speed
        if enemy_x[i] > player_x:
            new_x -= speed

        if enemy_y[i] < player_y:
            new_y += speed
        if enemy_y[i] > player_y:
            new_y -= speed

        if not is_wall(new_x,enemy_y[i]):
            enemy_x[i] = new_x

        if not is_wall(enemy_x[i],new_y):
            enemy_y[i] = new_y

        if abs(enemy_x[i]-player_x) < 6 and abs(enemy_y[i]-player_y) < 6:

            if pyxel.frame_count % 20 == 0:

                if player_armor > 0:
                    player_armor -= 2
                    pyxel.play(0, 2)
                else:
                    player_hp -= 2
                    pyxel.play(0, 2)


def boss_update():

    global boss_x, boss_y, boss_damage_timer, boss_flash,player_hp,player_armor

    if not boss_active:
        return

    speed = 0.3

    if boss_flash > 0:
        boss_flash -= 1

    # movement (same as before, with wall breaking)
    if boss_x < player_x:
        new_x = boss_x + speed
    elif boss_x > player_x:
        new_x = boss_x - speed
    else:
        new_x = boss_x

    if boss_y < player_y:
        new_y = boss_y + speed
    elif boss_y > player_y:
        new_y = boss_y - speed
    else:
        new_y = boss_y

    if not is_wall(new_x, boss_y):
        boss_x = new_x
    else:
        destroy_wall(new_x, boss_y)

    if not is_wall(boss_x, new_y):
        boss_y = new_y
    else:
        destroy_wall(boss_x, new_y)
    if pyxel.frame_count % 90 == 0:
        boss_shoot()
    # damage player
    if abs(boss_x-player_x) < 8 and abs(boss_y-player_y) < 8:

        if pyxel.frame_count % 15 == 0:

            if player_armor > 0:
                player_armor -= 1
                pyxel.play(0, 2)
            else:
                player_hp -= 1
                pyxel.play(0, 2)
                

def check_rooms():

    global area, map_data, boss_active, player_x, game_state

    map_width = len(map_data[0]) * TILE_SIZE

    if player_x > map_width - 12:

        # normal progression
        if area < 2:

            area += 1
            map_data = MAPS[area]
            reset_memory()

            spawn_level()
            spawn_key()
            spawn_ammo()
            spawn_component()

            player_x = 10

        # entering boss level
        elif area == 2:

            # ❌ NOT ENOUGH COMPONENTS → RESET GAME
            if components < 3:
                show_message("You failed to gather the cure...")
                reset_game()
                return

            # ✅ HAS ALL COMPONENTS → continue
            area = 3
            map_data = MAPS[area]
            reset_memory()

            boss_active = True

            spawn_level()
            spawn_ammo()

            show_message("Destroy the mutation core")
            player_x = 10

wall_health = {}  # dictionary to track wall tile health

def destroy_wall(x, y):
    global map_data, wall_health

    tile_x = int(x // TILE_SIZE)
    tile_y = int(y // TILE_SIZE)

    if map_data[tile_y][tile_x] == "1":
        key = (tile_x, tile_y)
        if key not in wall_health:
            wall_health[key] = 3  # wall needs 3 hits to break
        wall_health[key] -= 1
        if wall_health[key] <= 0:
            # destroy the wall
            row = list(map_data[tile_y])
            row[tile_x] = "0"
            map_data[tile_y] = "".join(row)
            del wall_health[key]
def boss_bullets_update():

    global player_hp, player_armor

    for i in reversed(range(len(boss_bullet_x))):

        boss_bullet_x[i] += boss_bullet_dx[i]
        boss_bullet_y[i] += boss_bullet_dy[i]

        # remove if hits wall
        if is_wall(boss_bullet_x[i], boss_bullet_y[i]):

            boss_bullet_x.pop(i)
            boss_bullet_y.pop(i)
            boss_bullet_dx.pop(i)
            boss_bullet_dy.pop(i)
            continue

        # hit player
        if abs(boss_bullet_x[i]-player_x) < 6 and abs(boss_bullet_y[i]-player_y) < 6:

            if player_armor > 0:
                player_armor -= 1
            else:
                player_hp -= 1

            boss_bullet_x.pop(i)
            boss_bullet_y.pop(i)
            boss_bullet_dx.pop(i)
            boss_bullet_dy.pop(i)

def update():

    global game_state, message_timer
    if pyxel.btnp(pyxel.KEY_R):
        reset_game()
    if game_state == "intro":
        update_intro()
        return

    if game_state == "win" or game_state == "lose":
        return

    player_update()
    enemies_update()

    bullets_update()
    bullet_hits()

    boss_update()
    # boss death
    if boss_active and boss_hp <= 0:
        game_state = "win"
    boss_bullets_update()

    check_key()
    unlock_door()

    check_ammo()
    check_component()

    check_rooms()

    camera_update()

    # ✅ ADD THIS HERE
    if player_hp <= 0:
        game_state = "lose"

    if message_timer > 0:
        message_timer -= 1
# -------------------------
# DRAW
# -------------------------
def draw_ui():

    # HP
    pyxel.text(5,5,"HP",7)

    for i in range(player_hp):
        pyxel.rect(20+i*6,5,5,4,8)

    # armor
    pyxel.text(5,12,"AR:"+str(player_armor),7)
    #ammo
    pyxel.text(5,33,"AMMO:"+str(ammo),7)

    for i in range(player_armor):
        pyxel.rect(20+i*6,12,5,4,13)

    # infection
    pyxel.text(5,19,"INF",7)

    pyxel.rect(20,19,player_infection,4,3)

    # components
    pyxel.text(5,26,"CURE:"+str(components)+"/3",7)
    
def draw_player():

    x = player_x - camera_x
    y = player_y - camera_y

    # head
    pyxel.rect(x+2,y,4,3,7)

    # eyes
    pyxel.pset(x+3,y+1,0)
    pyxel.pset(x+5,y+1,0)

    # body
    pyxel.rect(x+2,y+3,4,4,11)

    # arms
    pyxel.pset(x+1,y+4,11)
    pyxel.pset(x+6,y+4,11)

    # legs
    pyxel.pset(x+3,y+7,3)
    pyxel.pset(x+5,y+7,3)
def draw_win():
    pyxel.cls(0)
    pyxel.text(60,50,"YOU WIN",10)
    pyxel.text(40,70,"Press R to restart",7)


def draw_lose():
    pyxel.cls(0)
    pyxel.text(60,50,"YOU DIED",8)
    pyxel.text(40,70,"Press R to restart",7)


def draw_message():

    if message_timer > 0:
        pyxel.rect(10, HEIGHT-30, WIDTH-20, 20, 1)
        pyxel.text(20, HEIGHT-22, current_message, 7)
    
def draw_enemies():

    for i in range(len(enemy_x)):

        if enemy_hp[i] <= 0:
            continue

        ex = int(enemy_x[i] // TILE_SIZE)
        ey = int(enemy_y[i] // TILE_SIZE)

        # safety check (VERY important)
        if not (0 <= ey < len(map_memory) and 0 <= ex < len(map_memory[0])):
            continue

        # only draw enemies in seen areas
        if not map_memory[ey][ex]:
            continue

        x = enemy_x[i] - camera_x
        y = enemy_y[i] - camera_y

        if enemy_type[i] == "walker":

            pyxel.rect(x+2, y, 4, 3, 3)

            pyxel.pset(x+3, y+1, 0)
            pyxel.pset(x+5, y+1, 0)

            pyxel.rect(x+2, y+3, 4, 4, 3)

            pyxel.pset(x+3, y+7, 4)
            pyxel.pset(x+5, y+7, 4)

        else:

            # red fast mutant
            pyxel.rect(x+2,y,4,3,8)

            pyxel.pset(x+3,y+1,7)
            pyxel.pset(x+5,y+1,7)

            pyxel.rect(x+2,y+3,4,4,8)

            pyxel.pset(x+1,y+4,8)
            pyxel.pset(x+6,y+4,8)

            pyxel.pset(x+3,y+7,2)
            pyxel.pset(x+5,y+7,2)
def draw_boss_hp():
    if boss_active and boss_hp > 0:

        pyxel.rect(20, 5, 120, 6, 7)

        max_hp = 30
        hp_width = int(120 * (boss_hp / max_hp))

        pyxel.rect(20, 5, hp_width, 6, 8)

        pyxel.text(145, 5, "BOSS", 7)
def draw_boss():

    if not boss_active or boss_hp <= 0:
        return

    bx = int(boss_x // TILE_SIZE)
    by = int(boss_y // TILE_SIZE)

    if not (0 <= by < len(map_memory) and 0 <= bx < len(map_memory[0])):
        return

    if not map_memory[by][bx]:
        return

    x = boss_x - camera_x
    y = boss_y - camera_y

    color = 10
    if boss_flash > 0:
        color = 7

    # head
    pyxel.rect(x+1, y, 6, 3, color)

    # eyes
    pyxel.pset(x+2, y+1, 0)
    pyxel.pset(x+5, y+1, 0)

    # mouth
    pyxel.rect(x+3, y+2, 2, 1, 8)

    # body
    pyxel.rect(x, y+3, 8, 4, color)

    # arms
    pyxel.pset(x, y+4, color)
    pyxel.pset(x+7, y+4, color)

    # legs
    pyxel.pset(x+2, y+7, 5)
    pyxel.pset(x+5, y+7, 5)
    
def draw_world():

    for row in range(len(map_data)):
        for col in range(len(map_data[row])):

            tile = map_data[row][col]

            world_x = col * TILE_SIZE
            world_y = row * TILE_SIZE

            screen_x = world_x - camera_x
            screen_y = world_y - camera_y

            seen = map_memory[row][col]

            # never seen → full fog
            if not seen:
                pyxel.rect(screen_x, screen_y, 8, 8, 0)
                continue

            # seen before → dark memory shading
            if tile == "1":
                pyxel.rect(screen_x, screen_y, 8, 8, 5)
            elif tile == "2":
                pyxel.rect(screen_x, screen_y, 8, 8, 9)
            elif tile == "3":
                pyxel.rect(screen_x, screen_y, 8, 8, 8)
            else:
                pyxel.rect(screen_x, screen_y, 8, 8, 1)
def draw_bullets():

    for i in range(len(bullet_x)):

        x = bullet_x[i] - camera_x
        y = bullet_y[i] - camera_y

        pyxel.pset(x,y,7)
        pyxel.pset(x+1,y,6)
def draw_boss_bullets():

    for i in range(len(boss_bullet_x)):

        x = boss_bullet_x[i] - camera_x
        y = boss_bullet_y[i] - camera_y

        pyxel.rect(x,y,3,3,2)
def draw_key():

    if map_has_locked_door() and not has_key:

        pyxel.rect(key_x - camera_x,key_y - camera_y,4,4,10)
        show_message("Find the key")

def draw_intro():

    pyxel.cls(0)

    for i, line in enumerate(intro_text):
        x = WIDTH // 2 - len(line) * 2
        y = 30 + i * 10
        pyxel.text(x, y, line, 7)
def draw():

    if game_state == "intro":
        draw_intro()
        return

    if game_state == "win":
        draw_win()
        return

    if game_state == "lose":
        draw_lose()
        return

    pyxel.cls(0)

    draw_world()

    draw_player()
    draw_enemies()
    draw_boss()
    draw_bullets()
    draw_boss_bullets()

    draw_key()
    draw_ammo()
    draw_component()

    draw_ui()
    draw_message()

    if boss_active:
        draw_boss_hp()
# -------------------------
# START
# -------------------------

pyxel.init(WIDTH,HEIGHT,title="Mutation")

spawn_level()
spawn_key()
spawn_component()

pyxel.run(update,draw)