import pyxel
import random
import math

W = 180
H = 140
GROUND = 116
PW = 8
PH = 14

state = "title"
form = 0
level_i = 0
hp = 3
relic_i = 0

px = 18
py = GROUND - PH
vy = 0
grounded = True
duck = False
hurt = 0
cam = 0
shake = 0
flash = 0

mirror = False
moon = False
thorn = False
echo = None
levels = []

stars = []
dust = []
embers = []
wisps = []

relics = [
    {"name": "Mirror Needle", "good": "Run faster", "bad": "A shadow hunts you"},
    {"name": "Moon Tear", "good": "Jump higher", "bad": "Monsters move faster"},
    {"name": "Final Ember", "good": "Run faster + jump higher", "bad": "More monsters, faster monsters, more hanging thorns"},
]

base = [
    {
        "name": "Ivy Stair",
        "dark": 0,
        "length": 340,
        "monsters": [
            {"x": 96, "s": 96, "r": 30, "v": 1.0, "d": 1, "t": 0, "w": 12, "h": 8},
            {"x": 238, "s": 238, "r": 26, "v": 1.0, "d": -1, "t": 1, "w": 12, "h": 8},
        ],
        "hangers": [
            {"x": 164, "y": 96, "w": 30, "h": 10},
        ],
    },
    {
        "name": "Thorn Hall",
        "dark": 1,
        "length": 410,
        "monsters": [
            {"x": 100, "s": 100, "r": 32, "v": 1.0, "d": 1, "t": 2, "w": 12, "h": 8},
            {"x": 286, "s": 286, "r": 32, "v": 1.0, "d": -1, "t": 3, "w": 13, "h": 8},
        ],
        "hangers": [
            {"x": 146, "y": 96, "w": 30, "h": 10},
            {"x": 320, "y": 96, "w": 30, "h": 10},
        ],
    },
    {
        "name": "Moonless Keep",
        "dark": 2,
        "length": 520,
        "monsters": [
            {"x": 112, "s": 112, "r": 34, "v": 1.0, "d": 1, "t": 2, "w": 12, "h": 8},
            {"x": 272, "s": 272, "r": 34, "v": 1.0, "d": -1, "t": 3, "w": 13, "h": 8},
            {"x": 430, "s": 430, "r": 30, "v": 1.0, "d": 1, "t": 0, "w": 14, "h": 8},
        ],
        "hangers": [
            {"x": 150, "y": 96, "w": 30, "h": 10},
            {"x": 230, "y": 96, "w": 30, "h": 10},
            {"x": 320, "y": 96, "w": 30, "h": 10},
        ],
    },
]


def init_stars():
    global stars
    stars = []
    for _ in range(42):
        stars.append({
            "x": random.randint(0, W - 1),
            "y": random.randint(0, 60),
            "t": random.randint(20, 70),
            "p": random.randint(0, 69),
        })


def init_wisps():
    global wisps
    wisps = []
    for i in range(8):
        wisps.append({
            "x": random.randint(0, W - 1),
            "y": random.randint(40, H - 16),
            "vx": random.choice([0.15, 0.2, 0.25]),
            "phase": i * 8,
        })


def spawn_dust(x, y, n=3):
    for _ in range(n):
        dust.append({
            "x": x + random.uniform(-2, 2),
            "y": y + random.uniform(-1, 1),
            "vx": random.uniform(-0.6, 0.6),
            "vy": random.uniform(-0.8, -0.2),
            "life": random.randint(10, 18),
            "c": random.choice([5, 6, 7, 13]),
        })


def spawn_embers(x, y, n=10):
    for _ in range(n):
        embers.append({
            "x": x + random.uniform(-4, 4),
            "y": y + random.uniform(-4, 4),
            "vx": random.uniform(-1.0, 1.0),
            "vy": random.uniform(-1.8, -0.2),
            "life": random.randint(12, 24),
            "c": random.choice([8, 9, 10, 7]),
        })


def copy_levels():
    global levels
    levels = []
    for a in base:
        levels.append({
            "name": a["name"],
            "length": a["length"],
            "dark": a["dark"],
            "monsters": [m.copy() for m in a["monsters"]],
            "hangers": [d.copy() for d in a["hangers"]],
        })


def apply_memory():
    copy_levels()

    if mirror:
        levels[0]["dark"] = 1

    if moon:
        for m in levels[1]["monsters"]:
            m["v"] += 0.55

    if thorn:
        levels[2]["monsters"].append(
            {"x": 360, "s": 360, "r": 28, "v": 1.5, "d": 1, "t": 1, "w": 12, "h": 8}
        )
        for m in levels[2]["monsters"]:
            m["v"] += 0.55
        levels[2]["hangers"] += [
            {"x": 110, "y": 96, "w": 30, "h": 10},
            {"x": 395, "y": 96, "w": 30, "h": 10},
        ]


def lv():
    return levels[level_i]


def player_h():
    if duck and grounded:
        return 7
    return PH


def speed():
    if thorn:
        return 2.35
    if mirror:
        return 2.2
    return 1.75


def jump_power():
    if thorn:
        return -6.35
    if moon:
        return -6.15
    return -5.15


def can_clear_hangers():
    return (level_i == 1 and moon) or (level_i == 2 and thorn)


def hit(a, b, c, d, e, f, g, h):
    return a < e + g and a + c > e and b < f + h and b + d > f


def setup_audio():
    pyxel.sounds[0].set("e4g4", "t", "4", "f", 10)
    pyxel.sounds[1].set("c3", "s", "2", "f", 8)
    pyxel.sounds[2].set("a2f2d2", "n", "433", "f", 14)
    pyxel.sounds[3].set("c4e4g4b4", "t", "3332", "f", 18)
    pyxel.sounds[4].set("a2b2c3e3d3c3b2e3", "s", "11111111", "ffffffff", 40)


def ensure_music():
    if state in ["play", "relic"]:
        if pyxel.play_pos(3) is None:
            pyxel.play(3, 4, loop=True)


def reset_player():
    global px, py, vy, grounded, duck, hurt, cam
    px = 18
    py = GROUND - PH
    vy = 0
    grounded = True
    duck = False
    hurt = 0
    cam = 0


def setup_level():
    global echo
    reset_player()
    echo = None
    if level_i == 0 and mirror:
        echo = {"x": 120, "y": GROUND - PH, "d": 1, "s": 120, "r": 46, "v": 1.0}


def reset_game():
    global state, level_i, hp, mirror, moon, thorn, relic_i, shake, flash
    state = "title"
    level_i = 0
    hp = 3
    mirror = False
    moon = False
    thorn = False
    relic_i = 0
    shake = 0
    flash = 0
    dust.clear()
    embers.clear()
    pyxel.stop()
    copy_levels()
    setup_level()


def start_run():
    global state, level_i, hp, mirror, moon, thorn, relic_i, shake, flash
    state = "relic"
    level_i = 0
    hp = 3
    mirror = False
    moon = False
    thorn = False
    relic_i = 0
    shake = 0
    flash = 0
    dust.clear()
    embers.clear()
    copy_levels()
    setup_level()


def damage():
    global hp, hurt, vy, px, py, state, shake, flash
    if hurt > 0:
        return
    hp -= 1
    hurt = 26
    shake = 10
    flash = 4
    vy = -1
    px = max(0, px - 18)
    py = GROUND - player_h()
    spawn_embers(px, py + 8, 10)
    pyxel.play(1, 2)
    if hp <= 0:
        state = "dead"


def update_stars():
    for s in stars:
        s["p"] = (s["p"] + 1) % s["t"]


def update_wisps():
    for w in wisps:
        w["x"] += w["vx"]
        if w["x"] > W + 8:
            w["x"] = -8
            w["y"] = random.randint(40, H - 16)
        w["phase"] += 1


def update_particles():
    for p in dust[:]:
        p["x"] += p["vx"]
        p["y"] += p["vy"]
        p["life"] -= 1
        if p["life"] <= 0:
            dust.remove(p)

    for p in embers[:]:
        p["x"] += p["vx"]
        p["y"] += p["vy"]
        p["vy"] += 0.04
        p["life"] -= 1
        if p["life"] <= 0:
            embers.remove(p)


def update_player():
    global px, py, vy, grounded, duck, cam

    old_duck = duck
    old_grounded = grounded
    duck = pyxel.btn(pyxel.KEY_DOWN) and grounded

    if duck and not old_duck:
        pyxel.play(0, 1)

    if grounded:
        py = GROUND - player_h()

    moving = False
    if pyxel.btn(pyxel.KEY_LEFT):
        px -= speed()
        moving = True
    if pyxel.btn(pyxel.KEY_RIGHT):
        px += speed()
        moving = True

    if moving and grounded and pyxel.frame_count % 8 == 0:
        spawn_dust(px + 4, GROUND, 2)

    if pyxel.btnp(pyxel.KEY_UP) and grounded and not duck:
        vy = jump_power()
        grounded = False
        spawn_dust(px + 4, GROUND, 4)
        pyxel.play(0, 0)

    if not grounded:
        vy += 0.28
        py += vy
        if py + player_h() >= GROUND:
            py = GROUND - player_h()
            vy = 0
            grounded = True
            if not old_grounded:
                spawn_dust(px + 4, GROUND, 5)
    else:
        vy = 0

    px = max(0, min(px, lv()["length"] - PW))
    cam = max(0, min(px - W // 2, lv()["length"] - W))


def update_monsters():
    for m in lv()["monsters"]:
        m["x"] += m["d"] * m["v"]
        if m["x"] <= m["s"] - m["r"]:
            m["x"] = m["s"] - m["r"]
            m["d"] = 1
        if m["x"] >= m["s"] + m["r"]:
            m["x"] = m["s"] + m["r"]
            m["d"] = -1


def update_echo():
    if echo:
        echo["x"] += echo["d"] * echo["v"]
        if echo["x"] <= echo["s"] - echo["r"]:
            echo["x"] = echo["s"] - echo["r"]
            echo["d"] = 1
        if echo["x"] >= echo["s"] + echo["r"]:
            echo["x"] = echo["s"] + echo["r"]
            echo["d"] = -1


def hanger_hit(d):
    overlap_x = px < d["x"] + d["w"] and px + PW > d["x"]
    if not overlap_x:
        return False

    thorn_top = d["y"]
    thorn_bottom = d["y"] + d["h"]
    player_top = py
    player_bottom = py + player_h()

    duck_safe = duck and grounded and player_top >= thorn_bottom
    jump_safe = can_clear_hangers() and player_bottom <= thorn_top

    return not (duck_safe or jump_safe)


def check_hits():
    global hurt, state, level_i, relic_i, flash

    if hurt > 0:
        hurt -= 1

    if hurt == 0:
        for m in lv()["monsters"]:
            if hit(px, py, PW, player_h(), m["x"], GROUND - m["h"], m["w"], m["h"]):
                damage()
                return

        for d in lv()["hangers"]:
            if hanger_hit(d):
                damage()
                return

        if echo and hit(px, py, PW, player_h(), echo["x"], echo["y"], PW, PH):
            damage()
            return

    if hit(px, py, PW, player_h(), lv()["length"] - 24, GROUND - 22, 20, 22):
        pyxel.play(2, 3)
        level_i += 1
        flash = 5
        if level_i >= 3:
            state = "win"
        else:
            relic_i = level_i
            state = "relic"


def update():
    global state, form, mirror, moon, thorn, shake, flash

    ensure_music()
    update_stars()
    update_wisps()
    update_particles()

    if shake > 0:
        shake -= 1
    if flash > 0:
        flash -= 1

    if state == "title":
        if pyxel.btnp(pyxel.KEY_LEFT):
            form = 0
            start_run()
        if pyxel.btnp(pyxel.KEY_RIGHT):
            form = 1
            start_run()
        return

    if state == "relic":
        if pyxel.btnp(pyxel.KEY_UP):
            if relic_i == 0:
                mirror = True
            elif relic_i == 1:
                moon = True
            else:
                thorn = True
            apply_memory()
            setup_level()
            state = "play"
        if pyxel.btnp(pyxel.KEY_DOWN):
            apply_memory()
            setup_level()
            state = "play"
        return

    if state == "dead":
        if pyxel.btnp(pyxel.KEY_UP):
            reset_game()
        return

    if state == "win":
        if pyxel.btnp(pyxel.KEY_UP):
            reset_game()
        return

    update_player()
    update_monsters()
    update_echo()
    check_hits()


def draw_starfield():
    for s in stars:
        c = 7 if s["p"] < s["t"] // 2 else 12
        pyxel.pset(s["x"], s["y"], c)


def draw_wisps():
    for w in wisps:
        y = int(w["y"] + math.sin(w["phase"] / 12) * 3)
        x = int(w["x"])
        pyxel.pset(x, y, 12)
        pyxel.pset(x + 1, y, 7)
        pyxel.pset(x, y + 1, 13)


def draw_mountains():
    pts1 = [0, 18, 38, 55, 70, 88, 105, 124, 146, 165, 180]
    pts2 = [0, 12, 26, 40, 60, 82, 100, 120, 138, 160, 180]
    for i in range(len(pts1) - 1):
        pyxel.line(pts1[i], 66, pts1[i + 1], 46 + (i % 3) * 6, 5)
        pyxel.line(pts2[i], 72, pts2[i + 1], 56 + (i % 2) * 5, 1)


def draw_small_letter(x, y, ch, c):
    if ch == "L":
        pyxel.rect(x, y, 1, 6, c)
        pyxel.rect(x, y + 5, 4, 1, c)
    elif ch == "E":
        pyxel.rect(x, y, 1, 6, c)
        pyxel.rect(x, y, 4, 1, c)
        pyxel.rect(x, y + 2, 3, 1, c)
        pyxel.rect(x, y + 5, 4, 1, c)
    elif ch == "F":
        pyxel.rect(x, y, 1, 6, c)
        pyxel.rect(x, y, 4, 1, c)
        pyxel.rect(x, y + 2, 3, 1, c)
    elif ch == "T":
        pyxel.rect(x, y, 4, 1, c)
        pyxel.rect(x + 1, y, 1, 6, c)
    elif ch == "R":
        pyxel.rect(x, y, 1, 6, c)
        pyxel.rect(x, y, 3, 1, c)
        pyxel.rect(x, y + 2, 3, 1, c)
        pyxel.rect(x + 2, y + 1, 1, 2, c)
        pyxel.line(x + 1, y + 3, x + 3, y + 5, c)
    elif ch == "I":
        pyxel.rect(x, y, 4, 1, c)
        pyxel.rect(x + 1, y, 1, 6, c)
        pyxel.rect(x, y + 5, 4, 1, c)
    elif ch == "G":
        pyxel.rect(x, y, 4, 1, c)
        pyxel.rect(x, y + 5, 4, 1, c)
        pyxel.rect(x, y, 1, 6, c)
        pyxel.rect(x + 3, y + 3, 1, 2, c)
        pyxel.rect(x + 2, y + 3, 2, 1, c)
    elif ch == "H":
        pyxel.rect(x, y, 1, 6, c)
        pyxel.rect(x + 3, y, 1, 6, c)
        pyxel.rect(x, y + 2, 4, 1, c)


def draw_small_word(x, y, word):
    ox = x
    for ch in word:
        draw_small_letter(ox, y, ch, 7)
        ox += 6


def draw_player(x, y, is_duck, look):
    skin = 15
    eye = 0

    if look == 0:
        hair1 = 9
        hair2 = 4
        body = 2
        boot = 3
    else:
        hair1 = 8
        hair2 = 2
        body = 12
        boot = 7

    if is_duck:
        pyxel.rect(x + 1, y + 8, 6, 1, hair1)
        pyxel.rect(x + 1, y + 9, 6, 2, skin)
        pyxel.rect(x + 1, y + 11, 6, 2, body)
        pyxel.pset(x + 3, y + 10, eye)
        pyxel.pset(x + 5, y + 10, eye)
        pyxel.line(x + 1, y + 13, x + 6, y + 13, boot)
        return

    pyxel.rect(x + 2, y + 0, 4, 1, hair2)
    pyxel.rect(x + 1, y + 1, 6, 2, hair1)
    pyxel.rect(x + 2, y + 3, 4, 4, skin)
    pyxel.pset(x + 3, y + 4, eye)
    pyxel.pset(x + 5, y + 4, eye)
    pyxel.rect(x + 2, y + 7, 4, 5, body)
    pyxel.line(x + 1, y + 7, x + 2, y + 9, skin)
    pyxel.line(x + 6, y + 7, x + 7, y + 9, skin)
    pyxel.line(x + 3, y + 12, x + 2, y + 15, boot)
    pyxel.line(x + 5, y + 12, x + 6, y + 15, boot)
    pyxel.pset(x + 3, y + 8, 7)
    pyxel.pset(x + 4, y + 9, 7)


def draw_monster(x, y, t, w, h):
    if t == 0:
        pyxel.rect(x + 1, y + 2, w - 2, h - 2, 3)
        pyxel.rectb(x + 1, y + 2, w - 2, h - 2, 11)
        pyxel.line(x + 3, y + 1, x + 2, y - 2, 11)
        pyxel.line(x + w - 4, y + 1, x + w - 3, y - 2, 11)
        pyxel.line(x + 3, y + h, x + 2, y + h + 2, 11)
        pyxel.line(x + w - 4, y + h, x + w - 3, y + h + 2, 11)
        pyxel.pset(x + 3, y + 4, 7)
        pyxel.pset(x + w - 4, y + 4, 7)
    elif t == 1:
        pyxel.rect(x + 1, y + 1, w - 2, h - 1, 15)
        pyxel.rectb(x + 1, y + 1, w - 2, h - 1, 13)
        pyxel.line(x + 2, y + h, x + 1, y + h + 2, 13)
        pyxel.line(x + w - 3, y + h, x + w - 2, y + h + 2, 13)
        pyxel.line(x + 2, y + 2, x + 1, y + 1, 13)
        pyxel.line(x + w - 3, y + 2, x + w - 2, y + 1, 13)
        pyxel.pset(x + 3, y + 4, 8)
        pyxel.pset(x + w - 4, y + 4, 8)
    elif t == 2:
        pyxel.rect(x + 2, y + 2, w - 4, h - 2, 0)
        pyxel.rectb(x + 2, y + 2, w - 4, h - 2, 5)
        pyxel.line(x + 4, y + 1, x + 2, y - 2, 13)
        pyxel.line(x + w - 5, y + 1, x + w - 3, y - 2, 13)
        pyxel.line(x + 2, y + h, x + 4, y + h + 2, 5)
        pyxel.line(x + w - 3, y + h, x + w - 5, y + h + 2, 5)
        pyxel.pset(x + 4, y + 4, 10)
        pyxel.pset(x + w - 5, y + 4, 10)
    else:
        pyxel.rect(x + 1, y + 3, w - 2, h - 3, 1)
        pyxel.rectb(x + 1, y + 3, w - 2, h - 3, 12)
        pyxel.line(x + 3, y + 2, x + 1, y, 7)
        pyxel.line(x + w - 4, y + 2, x + w - 2, y, 7)
        pyxel.line(x + 2, y + h, x + 2, y + h + 2, 12)
        pyxel.line(x + w - 3, y + h, x + w - 3, y + h + 2, 12)
        pyxel.pset(x + 3, y + 5, 7)
        pyxel.pset(x + w - 4, y + 5, 7)


def draw_heart(x, y, c):
    pyxel.pset(x + 1, y, c)
    pyxel.pset(x + 3, y, c)
    pyxel.rect(x, y + 1, 5, 2, c)
    pyxel.rect(x + 1, y + 3, 3, 1, c)
    pyxel.pset(x + 2, y + 4, c)


def draw_vines():
    for i in range(8 + level_i * 2):
        vx = (i * 25 - cam // 2) % (W + 40) - 10
        top = 10 + (i * 7) % 24
        length = 22 + (i * 11) % 36
        pyxel.line(vx, top, vx, top + length, 3)
        pyxel.pset(vx - 1, top + 5, 11)
        pyxel.pset(vx + 1, top + 10, 11)
        pyxel.pset(vx - 1, top + 15, 11)
        pyxel.pset(vx + 1, top + 20, 11)


def draw_windows():
    rows = [28, 50]
    cols = [30, 54, 78, 102, 126]
    for wy in rows:
        for wx in cols:
            pyxel.rect(wx, wy, 10, 14, 1)
            pyxel.rectb(wx, wy, 10, 14, 13)
            pyxel.line(wx + 5, wy, wx + 5, wy + 14, 13)
            pyxel.line(wx, wy + 7, wx + 9, wy + 7, 13)
            pyxel.rect(wx + 2, wy + 2, 2, 3, 10)
            pyxel.rect(wx + 6, wy + 2, 2, 3, 10)


def draw_chains():
    for cx in [24, 46, 134, 156]:
        for y in range(18, 96, 6):
            pyxel.pset(cx, y, 13)
            pyxel.pset(cx + 1, y + 2, 13)


def draw_castle_columns():
    for x in [22, 44, 136, 158]:
        pyxel.rect(x, 18, 8, H - 18, 5)
        pyxel.rectb(x, 18, 8, H - 18, 13)
        pyxel.line(x + 2, 18, x + 2, H - 1, 11)


def draw_ground():
    x = 0
    while x < lv()["length"]:
        sx = x - cam
        if -8 <= sx <= W:
            pyxel.rect(sx, GROUND, 8, 8, 5)
            pyxel.rectb(sx, GROUND, 8, 8, 13)
            pyxel.line(sx, GROUND + 3, sx + 7, GROUND + 3, 11)
            pyxel.pset(sx + 2, GROUND + 1, 3)
            pyxel.pset(sx + 5, GROUND + 5, 3)
            pyxel.pset(sx + 1, GROUND + 6, 11)
        x += 8
    pyxel.line(0, GROUND, W, GROUND, 7)


def draw_hangers():
    for d in lv()["hangers"]:
        sx = d["x"] - cam
        pyxel.line(sx + 3, 0, sx + 3, d["y"], 3)
        pyxel.line(sx + d["w"] - 4, 0, sx + d["w"] - 4, d["y"], 3)
        pyxel.rect(sx, d["y"], d["w"], d["h"], 3)
        pyxel.rectb(sx, d["y"], d["w"], d["h"], 11)
        pyxel.pset(sx + 2, d["y"] + 1, 11)
        pyxel.pset(sx + d["w"] - 3, d["y"] + 1, 11)


def draw_shadow():
    if not echo:
        return
    ex = echo["x"] - cam
    ey = echo["y"]
    pyxel.rect(ex + 2, ey + 1, 4, 2, 5)
    pyxel.rect(ex + 1, ey + 3, 6, 4, 13)
    pyxel.rect(ex + 2, ey + 7, 4, 4, 5)
    pyxel.pset(ex + 3, ey + 4, 8)
    pyxel.pset(ex + 5, ey + 4, 8)
    pyxel.line(ex + 3, ey + 10, ex + 2, ey + 13, 13)
    pyxel.line(ex + 5, ey + 10, ex + 6, ey + 13, 13)
    pyxel.line(ex + 1, ey + 8, ex, ey + 10, 5)
    pyxel.line(ex + 7, ey + 8, ex + 8, ey + 10, 5)


def draw_portal():
    po = lv()["length"] - 24 - cam
    r1 = 12 + (pyxel.frame_count % 20) // 10
    r2 = 8 + (pyxel.frame_count % 16) // 8
    pyxel.circb(po + 10, GROUND - 11, r1, 9)
    pyxel.circb(po + 10, GROUND - 11, r2, 10)
    pyxel.circb(po + 10, GROUND - 11, 5, 7)
    pyxel.rect(po + 5, GROUND - 17, 10, 12, 1)
    pyxel.pset(po + 10, GROUND - 11, 7)
    pyxel.pset(po + 8, GROUND - 15, 10)
    pyxel.pset(po + 13, GROUND - 8, 10)
    pyxel.pset(po + 7, GROUND - 9, 7)
    pyxel.pset(po + 14, GROUND - 13, 7)


def draw_particles():
    for p in dust:
        pyxel.pset(int(p["x"] - cam), int(p["y"]), p["c"])
    for p in embers:
        pyxel.pset(int(p["x"] - cam), int(p["y"]), p["c"])


def draw_ui():
    pyxel.rect(0, 0, W, 12, 0)
    pyxel.text(4, 3, "L/R", 7)
    pyxel.text(28, 3, "UP JUMP", 7)
    pyxel.text(78, 3, "DOWN DUCK", 7)
    pyxel.text(138, 3, str(level_i + 1) + "/3", 7)
    pyxel.text(6, 130, lv()["name"], 7)
    for i in range(3):
        draw_heart(136 + i * 12, 130, 8 if i < hp else 13)


def draw_game():
    dark = lv()["dark"]
    pyxel.cls(0)

    ox = -1 if shake % 2 and shake > 0 else 1 if shake > 0 else 0

    pyxel.rect(0, 0, W, 18, 1 if dark == 0 else 0)
    pyxel.rect(0, 18, W, 24, 5 if dark < 2 else 1)
    pyxel.rect(0, 42, W, 30, 1)
    pyxel.rect(0, 72, W, H - 72, 0)

    draw_starfield()
    draw_mountains()
    draw_wisps()

    pyxel.circ(142, 18, 12, 7 if dark == 0 else 6)
    pyxel.circ(142, 18, 7, 10 if dark == 0 else 5)
    if dark == 2:
        pyxel.circ(147, 18, 9, 0)

    pyxel.rect(20, 18, W - 40, 78, 1)
    pyxel.rect(20, 96, W - 40, 44, 0)

    draw_castle_columns()
    draw_chains()
    draw_windows()
    draw_vines()
    draw_ground()
    draw_hangers()

    for m in lv()["monsters"]:
        draw_monster(m["x"] - cam + ox, GROUND - m["h"], m["t"], m["w"], m["h"])

    draw_shadow()
    draw_particles()

    if hurt == 0 or pyxel.frame_count % 4 < 2:
        draw_player(px - cam + ox, py, player_h() != PH, form)

    draw_portal()
    draw_ui()

    if flash > 0:
        pyxel.rectb(0, 0, W, H, 7)


def draw_title():
    pyxel.cls(0)
    pyxel.rect(0, 0, W, 22, 1)
    pyxel.rect(0, 22, W, 26, 5)
    pyxel.rect(0, 48, W, H - 48, 0)

    draw_starfield()
    draw_mountains()
    draw_wisps()

    pyxel.circ(145, 16, 12, 7)
    pyxel.circ(145, 16, 7, 10)

    pyxel.rect(16, 54, 56, 44, 1)
    pyxel.rectb(16, 54, 56, 44, 7)
    pyxel.rect(108, 54, 56, 44, 1)
    pyxel.rectb(108, 54, 56, 44, 7)
    pyxel.line(16, 99, 72, 99, 13)
    pyxel.line(108, 99, 164, 99, 13)

    pyxel.text(47, 14, "THE TOWER", 7)
    pyxel.text(53, 24, "REMEMBERS", 7)
    pyxel.text(42, 42, "Choose a form", 7)

    draw_player(40, 63, False, 0)
    draw_player(132, 63, False, 1)

    draw_small_word(40, 110, "LEFT")
    draw_small_word(124, 110, "RIGHT")


def draw_relic():
    r = relics[relic_i]
    pyxel.cls(0)
    pyxel.rect(0, 0, W, 35, 1)
    pyxel.rect(0, 35, W, 105, 0)
    pyxel.text(58, 12, "THE TOWER OFFERS", 7)
    pyxel.text(74, 24, "A RELIC", 7)
    pyxel.rect(14, 42, 152, 70, 1)
    pyxel.rectb(14, 42, 152, 70, 7)
    pyxel.text(22, 50, r["name"], 7)
    pyxel.text(20, 66, "Blessing:", 7)
    pyxel.text(20, 74, r["good"], 7)
    pyxel.text(20, 90, "Curse:", 7)
    pyxel.text(20, 98, r["bad"], 7)
    pyxel.text(20, 120, "UP=TAKE  DOWN=IGNORE", 7)


def draw_dead():
    pyxel.cls(0)
    pyxel.text(70, 60, "GAME OVER", 7)
    pyxel.text(40, 84, "Press UP for title", 7)


def draw_win():
    pyxel.cls(0)
    pyxel.text(68, 58, "YOU ESCAPED", 7)
    pyxel.text(34, 82, "But the tower remembers.", 7)
    pyxel.text(40, 104, "Press UP for title", 7)


def draw():
    if state == "title":
        draw_title()
    elif state == "relic":
        draw_relic()
    elif state == "dead":
        draw_dead()
    elif state == "win":
        draw_win()
    else:
        draw_game()


init_stars()
init_wisps()
pyxel.init(W, H, title="The Tower Remembers")
setup_audio()
reset_game()
pyxel.run(update, draw)