#############################################
#              LIEN PYXELSTUDIO             #
# https://www.pyxelstudio.net/studio/8y5q2t #
# https://www.pyxelstudio.net/studio/e8tpmc #
#############################################
import math
import pyxel
from random import *

pyxel.init(256, 256, "La Clef des Champs", 30)
pyxel.load("da.pyxres")

#############
# VARIABLES #
#############

VITESSE = 0.125

# position des objets
alien = [0.0, 14.0, 0]
clef = [3, 2]
bernacle = [7, 14]
porte = [15, 2]
abeille = [10, 10, 0]
boules_de_feu_liste = []
pics_liste = [[0, 6], [9, 2], [8, 14]]
clef_attrappee = False

# caractéristiques de l'alien
t_chute = 0
en_saut = False
vies = 2
t_immunite = 0
gameover = False

# monde : 0 pour case vide, 1 pour plateforme, 2 pour échelle, 3 pour plateforme-echelle
# le monde fait 16 de large et 16 de haut
monde = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 1, 1, 1, 1, 1, 3, 1, 0, 0, 0, 1, 3, 1],
    [0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0],
    [1, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 3, 1, 1, 1],
    [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 2, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0],
    [0, 1, 3, 1, 1, 1, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0],
    [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 3, 0],
    [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0],
    [0, 0, 2, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 2, 0],
    [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
]

############################
# FONCTIONS INTERMEDIAIRES #
############################


def deplacement_alien():
    """déplacement de l'alien avec les flèches et la barre espace pour sauter"""
    global en_saut
    # a droite
    if pyxel.btn(pyxel.KEY_RIGHT) and alien[0] + VITESSE <= 15:
        alien[2] = 0
        alien[0] += VITESSE
    # a gauche
    if pyxel.btn(pyxel.KEY_LEFT) and alien[0] - VITESSE >= 0:
        alien[2] = 2
        alien[0] -= VITESSE
    # en haut
    if pyxel.btn(pyxel.KEY_UP) and estSurUneEchelle():
        alien[2] = 1
        alien[1] -= VITESSE
        alien[0] = int(alien[0] + 0.4)
    # en bas
    if pyxel.btn(pyxel.KEY_DOWN) and estSurUneEchelle():
        alien[2] = 1
        alien[1] += VITESSE
        alien[0] = int(alien[0] + 0.4)
    # mise à jour de en_saut
    if estSurUnePlateforme() or estSurUneEchelle():
        en_saut = False
    # on saute
    if pyxel.btn(pyxel.KEY_SPACE) and estSurUnePlateforme() and not estSurUneEchelle():
        en_saut = True
    # mise à jour de la position avec le en_saut
    if en_saut:
        alien[1] -= 0.5


def estSurUnePlateforme():
    """Determine si le personnage est sur une plateforme"""
    # le personnage est sur une plateforme si au moins un bout de lui est sur une plateforme
    # si ses coordonnées sont entières et
    # le coin inférieur gauche (monde[y+1][x]) est sur une plateforme
    # ou le coin inférieur droit (monde[y+1][x+1]) est sur une plateforme
    return int(alien[1]) == alien[1] and (
        monde[int(alien[1]) + 1][int(alien[0])] % 2 == 1
        or (
            int(alien[0]) != alien[0]
            and monde[int(alien[1]) + 1][int(alien[0]) + 1] % 2 == 1
        )
    )


def estSurUneEchelle():
    """Determine si le personnage est sur une échelle"""
    # le personnage est sur l'échelle si plus de la moitié de son corps (0.6) est sur l'échelle
    # si le coin inférieur gauche (monde[y+1][x]) est à moins de 0.4 latéralement d'une échelle
    # si le coin inférieur droit (monde[y+1][x+1]) est à moins de 0.4 latéralement d'une échelle
    return (
        monde[int(alien[1] - 0.1) + 1][int(alien[0] + 0.4)] // 2 == 1
        or monde[int(alien[1] - 0.1) + 1][int(alien[0] - 0.4) + 1] // 2 == 1
    )


def estDevantLaPorte():
    """Determine si le personnage est devant la porte"""
    # idem que pour échelle
    return porte == [int(alien[0] + 0.4), int(alien[1])] and porte == [
        int(alien[0] - 0.4) + 1,
        int(alien[1]),
    ]


def gravite():
    """Gestion de l'effet de gravité"""
    global t_chute
    if not (estSurUneEchelle() or estSurUnePlateforme()):
        # si le temps de chute est inférieur à 20 on ajoute 1 sinon on fait rien
        # (on ne va pas encore accélérer la chute)
        if t_chute <= 20:
            t_chute += 1
        # il tombe ; on teste tous les pouillèmes de cases pour savoir s'il est arrivé sur une plateforme
        for i in range(t_chute):
            alien[1] += i / 16
            if estSurUnePlateforme():
                return None  # retour anticipé : on s'arrête là
            alien[1] -= i / 16
        alien[1] += t_chute / 16
    else:
        t_chute = 0


def abeille_creation():
    "Choisit aléatoirement un y (raisonnable bien sûr) et une direction"
    # on définit ces coordonnées bidon pour être sûr de rentrer dans le while
    abeille[0] = alien[0]
    abeille[1] = alien[1]
    # on s'assure que l'abeille n'apparaîsse pas trop près du joueur
    while math.sqrt((alien[0] - abeille[0]) ** 2 + (alien[1] - abeille[1]) ** 2) < 3:
        # on détermine la hauteur de l'abeille
        abeille[1] = randint(2, 14)
        # on détermine la direction prise par l'abeille, 0=droite, 1=gauche
        abeille[2] = randint(0, 1)
    if abeille[2] == 0:
        # l'abeille apparaît à gauche
        abeille[0] = -1
    else:
        # l'abeille apparaît à droite
        abeille[0] = 17


def abeille_deplacement():
    "fait avancer latéralement l'abeille d'un pixel à chaque frame"
    if abeille[2] == 0:
        # si l'abeille va vers la droite, on ajoute 1 à son abscisse
        abeille[0] += VITESSE / 2
    else:
        # si l'abeille va vers la gauche, on soustrait 1 à son abscisse
        abeille[0] -= VITESSE / 2


def abeille_suppression():
    "Appelle la fonction abeille_creation si l'abeille sort du cadre pour en créer une nouvelle"
    if abeille[0] < -1 or abeille[0] > 17:
        abeille_creation()


def bernacle_deplacement():
    """Déplacement de la bernacle pour se rapprocher de l'abscisse de l'alien"""
    if bernacle[0] < alien[0]:  # Si la bernacle est à gauche du joueur
        bernacle[0] += VITESSE / 2  # Elle va vers la droite
    elif bernacle[0] > alien[0]:  # Si la bernacle est à droite du joueur
        bernacle[0] -= VITESSE / 2  # Elle va vers la gauche


def boule_de_feu_creation():
    """Création d'une boule de feu depuis la bernacle"""
    # Crée une boule de feu au dessus de la bernacle
    boules_de_feu_liste.append([bernacle[0] + 0.25, bernacle[1]])


def boule_de_feu_deplacement():
    """Déplacement des boules de feu"""
    for boule_de_feu in boules_de_feu_liste:
        boule_de_feu[1] -= VITESSE / 2


def boule_de_feu_suppression():
    """Suppression des boules de feu qui sortent de l'écran"""
    for boule_de_feu in boules_de_feu_liste:
        if not (0 <= boule_de_feu[0] <= 15 and 0 <= boule_de_feu[1] <= 15):
            del boule_de_feu


def touche_abeille():
    """Enlève une vie au joueur si il touche l'abeille"""
    global vies, t_immunite
    if abs(alien[0] - abeille[0]) < 0.9 and abs(alien[1] - abeille[1]) < 0.9:
        vies -= 1
        t_immunite = 45


def touche_boule_de_feu():
    """Enlève une vie au joueur si il touche une boule de feu et supprime la boule de feu"""
    global vies, t_immunite
    for boule in boules_de_feu_liste:
        if -0.9 < alien[0] - boule[0] < 0.4 and -0.9 < alien[1] - boule[1] < 0.4:
            vies -= 1
            t_immunite = 45
            del boule


def touche_bernacle():
    """Enlève une vie au joueur si il touche la bernacle"""
    global vies, t_immunite
    if abs(alien[0] - bernacle[0]) < 0.9 and abs(alien[1] - bernacle[1]) < 0.9:
        vies -= 1
        t_immunite = 45


def touche_pics():
    global vies, t_immunite
    """Enlève une vie au joueur si il touche des pics"""
    for pic in pics_liste:
        if abs(alien[0] - pic[0]) < 0.8 and -0.8 < alien[1] - pic[1] < 0.3:
            vies -= 1
            t_immunite = 45


def touche_clef():
    """Met à jour la variable clef_attrapee si le joueur touche la clef"""
    global clef_attrappee
    if abs(alien[0] - clef[0]) < 0.9 and abs(alien[1] - clef[1]) < 0.9:
        clef_attrappee = True


#########################
# FONCTIONS PRINCIPALES #
#########################


def update():
    """mise à jour des variables 30 fois par seconde"""

    global t_immunite, gameover
    # position de l'alien
    deplacement_alien()
    gravite()
    # position des monstres
    bernacle_deplacement()
    if pyxel.frame_count % 60 == 0:
        boule_de_feu_creation()
    boule_de_feu_deplacement()
    boule_de_feu_suppression()
    if pyxel.frame_count == 0:
        abeille_creation()
    abeille_deplacement()
    abeille_suppression()
    # vérifications pour la perte de vies
    if t_immunite == 0:
        touche_abeille()
        touche_bernacle()
        touche_boule_de_feu()
        touche_pics()
    # mise à jour du temps d'immunité restant
    if t_immunite > 0:
        t_immunite -= 1
    # mise à jour de gameover
    if vies <= 0:
        gameover = True
    # vérifications pour la clef
    touche_clef()
    # vérification pour la porte
    if estDevantLaPorte() and clef_attrappee:
        # on met tout en bleu foncé
        pyxel.cls(1)
        # on écrit gagne en vert
        pyxel.text(115, 125, "GAGNE", 11)
        # on fige l'écran
        pyxel.show()


def draw():
    """Dessin de la vue 30 fois par seconde"""

    if gameover:
        # on met tout en orange
        pyxel.cls(9)
        # on écrit game over en violet
        pyxel.text(109, 125, "GAME OVER", 2)
    else:
        # on efface tout
        pyxel.cls(0)

        ###################
        # dessin du monde #
        ###################

        for i in range(16):
            for j in range(16):
                pyxel.blt(i * 16, j * 16, 0, 16 * monde[j][i], 0, 16, 16)

        ###########################
        # dessin des objets fixes #
        ###########################

        # clef
        if not clef_attrappee:
            pyxel.blt(clef[0] * 16, clef[1] * 16, 0, 0, 64, 16, 16, 0)
        # porte
        pyxel.blt(porte[0] * 16, porte[1] * 16, 0, 0, 80, 16, 16, 1)
        # pics
        for pic in pics_liste:
            pyxel.blt(pic[0] * 16, (pic[1] + 0.5) * 16, 0, 0, 128, 16, 8, 0)

        ####################
        # dessin du joueur #
        ####################

        pyxel.blt(
            alien[0] * 16,
            alien[1] * 16,
            0,
            16 * alien[2],  # correspond aux orientations droite haut gauche
            16 * (1 + (alien[0] + alien[1]) * 10 // 2 % 3),  # (voir plus bas)
            16,
            16,
            0,
        )

        # choix des positions (détail de la procédure)
        # dans le fichier ressource, on a 3 carrés de 16x16 alignés verticalement pour chaque orientation
        # d'ordonnées respectives 16, 32, 48.
        # on a donc 16 * (1 + [nombre entre 0 et 2]) ; c'est ce nombre qui nous intérresse
        # on veut que tous les 2/10 de case, l'alien change de position
        # donc on fait * 10 pour que les dixièmes soient des entiers
        # puis // 2 pour que les cinquièmes soient des entiers
        # et % 3 pour que ce soit cyclique

        #############################
        # dessin des objets mobiles #
        #############################

        # abeille
        if abeille[2] == 0:
            # vers la droite
            pyxel.blt(abeille[0] * 16, abeille[1] * 16, 0, 16, 96, 16, 16, 14)
        else:
            # vers la gauche
            pyxel.blt(abeille[0] * 16, abeille[1] * 16, 0, 0, 96, 16, 16, 14)
        # bernacle
        pyxel.blt(bernacle[0] * 16, bernacle[1] * 16, 0, 0, 112, 16, 16, 1)
        # boules de feu
        for boule_de_feu in boules_de_feu_liste:
            pyxel.blt(boule_de_feu[0] * 16, boule_de_feu[1] * 16, 0, 16, 112, 8, 8, 0)

        #####################
        # dessin des coeurs #
        #####################

        for i in range(vies):
            pyxel.blt(i * 16, 0, 0, 0, 136, 16, 16, 0)


pyxel.run(update, draw)