/**************************************************************************
PLUSIEURS SPRITES ANIMES

          ce programme illustre l'animation de plusieurs personnage (animaux)
          qui traversent l'écran

          Pour avoir plusieurs séquences animées
          il faut plusieurs tableaux de bitmaps
          correspondant aux séquences d'images

          Ici, chaque structure de type t_sequence regroupe
          les images d'une séquence, et on utilise un tableau
          global tabSequences de ces structures

          Pour éviter d'avoir à gérer des quantités
          importantes de petits fichiers images, chaque
          séquence image est regroupée dans un même .bmp
          La procédure void chargerSequence(t_sequence * seq);
          permet de découper cette image de départ en petite bitmaps
          (une pour chaque étape de l'animation)
**************************************************************************/

#include <allegro.h>
#include <time.h>


/***************************************************/
/*                   CONSTANTES                    */
/*            devront aller dans un .h             */
/***************************************************/

// sur cet exemple Nombre d'acteurs fixe :
//    le tableau d'acteurs sera déclaré et utilisé en "automatique"
//    t_acteur tab[NACTEUR];
// Pour modifier cette valeur il faut adapter remplirTabActeurs
#define NACTEUR 6

// nombre total de séquences d'animation du jeu
// ( le nombre d'acteurs peut être supérieur si plusieurs acteurs utilisent les même graphismes )
// Pour modifier cette valeur il faut modifier le tableau initialisé tabSequences
#define NSEQUENCE 6


/*****************************/
/*       STRUCTURES          */
/*  devront aller dans un .h */
/*****************************/

// données pour chaque séquence d'animation chargée initialement
//  ( à ne faire qu'une seule fois au début du jeu )
typedef struct sequence
{
    char *nomSource; // nom du fichier image contenant la séquence
    int nimg;        // nombre d'images dans la séquence
    int tx,ty;       // largeur et hauteur des images de la séquence
    int ncol;        // nbr images cotes à cotes horizontalement dans le fichier image
    BITMAP **img;    // tableau de pointeurs pour indiquer les images
} t_sequence;

// données personnelles de chaque acteur qui se déplace
// sur cet exemple on ne gère que des déplacements horizontaux (pas de dy...)
typedef struct acteur
{
    // géométrie et déplacements

    int x,y;         // position du coin sup. gauche
    int dx;          // deplacement
    int tmpdx;       // ralentir déplacements en x (1 pour ne pas ralentir)
    int cptdx;       // compteur pour ralentir déplacement
    int tx,ty;       // largeur hauteur

    // séquence d'images de l'animation

    int imgcourante; // indice de l'image courante
    int tmpimg;      // ralentir séquence (image suivante 1 fois sur tmpimg)
    int cptimg;      // compteur pour ralentir séquence

    // type = numéro de la sequence à utiliser dans tabSequences
    // ( ici : 0 Dragon  1 Poisson  2 Crabe  3 Abeille  4 Moustique  5 Serpent )

    int type;

} t_acteur;


/*****************************/
/*       PROTOTYPES          */
/*  devront aller dans un .h */
/*****************************/

// Allouer et initialiser un acteur
t_acteur * creerActeur(int type, int x, int y, int dx, int tmpdx, int tmpimg);

// Pour remplir un tableau avec des acteurs créés
// Sur cet exemple on crée 6 acteurs, chacun associé à une séquence
void remplirTabActeurs(t_acteur * tab[NACTEUR]);


// Actualiser un acteur (bouger ...)
void actualiserActeur(t_acteur *acteur);

// Gérer l'évolution de l'ensemble des acteurs
void actualiserTabActeurs(t_acteur * tab[NACTEUR]);


// Dessiner un acteur sur une bitmap bmp
void dessinerActeur(BITMAP *bmp, t_acteur *acteur);

// Dessiner l'ensemble des acteurs sur une bitmap bmp
void dessinerTabActeurs(BITMAP *bmp,t_acteur * tab[NACTEUR]);


// Charger les images d'une séquence d'animation
// Découpe une image source en plusieurs vignettes
// (doivent être rangées de gauche à droite et de haut en bas)
void chargerSequence(t_sequence * seq);

// Charger toutes les séquences du tableau global tabSequence
void chargerTabSequences();



/***************************************************/
/*              VARIABLES GLOBALES                 */
/*  les déclarations devront aller dans un .h      */
/*  les définitions devront aller dans un .c       */
/***************************************************/


// tableau global de toutes les séquences animées du jeu
// on s'autorise à utiliser un tableau global car ces données
// n'existent qu'en un seul exemplaire à l'échelle du programme
t_sequence tabSequences[NSEQUENCE] =
{
        //          nomSource           , nimg,  tx,  ty, ncol
        { "images/dragon/dragon.bmp"    ,    6, 128,  64,    3 },
        { "images/dragon/poisson.bmp"   ,    3,  64,  32,    3 },
        { "images/dragon/crabe.bmp"     ,    4,  64,  32,    4 },
        { "images/dragon/abeille.bmp"   ,    6,  50,  40,    6 },
        { "images/dragon/moustique.bmp" ,    6,  50,  40,    6 },
        { "images/dragon/serpent.bmp"   ,    7, 100,  50,    4 }
};



/******************************************/
/* PROGRAMME PRINCIPAL                    */
/* initialisation puis boucle d'animation */
/******************************************/

int main()
{
    // Le tableau regroupant tous les acteurs
    // c'est un tableau de pointeurs sur structures t_acteurs
    t_acteur * mesActeurs[NACTEUR];

    // BITMAP servant de buffer d'affichage (double buffer)
    BITMAP *page;

    // Image de fond
    BITMAP *decor;

    // On va utiliser du hasard
    srand(time(NULL));

    // Lancer allegro et le mode graphique
    allegro_init();
    install_keyboard();

    set_color_depth(desktop_color_depth());
    if (set_gfx_mode(GFX_AUTODETECT_WINDOWED,640,480,0,0)!=0)
    {
        allegro_message("prb gfx mode");
        allegro_exit();
        exit(EXIT_FAILURE);
    }

    // Création du buffer d'affichage à la taille de l'écran
    page=create_bitmap(SCREEN_W,SCREEN_H);
    clear_bitmap(page);

    // charger image de fond
    decor=load_bitmap("images/dragon/decor.bmp",NULL);
    if (!decor)
    {
        allegro_message("pas pu trouver images/dragon/decor.bmp");
        exit(EXIT_FAILURE);
    }


    // Chargement des images des séquences animées
    chargerTabSequences();

    // Initialisation aléatoire des paramètres des acteurs :
    // remplir le tableau avec des acteurs alloués et initialisés
    remplirTabActeurs(mesActeurs);

    // Boucle d'animation (pas d'interaction)
    while (!key[KEY_ESC])
    {

        // 1)  EFFACER BUFFER, en appliquant décor  (pas de clear_bitmap)
        blit(decor,page,0,0,0,0,SCREEN_W,SCREEN_H);

        // 2) DETERMINER NOUVELLEs POSITIONs
        actualiserTabActeurs(mesActeurs);

        // 3) AFFICHAGE NOUVELLEs POSITIONs SUR LE BUFFER
        dessinerTabActeurs(page,mesActeurs);

        // 4) AFFICHAGE DU BUFFER MIS A JOUR A L'ECRAN
        blit(page,screen,0,0,0,0,SCREEN_W,SCREEN_H);

        // 5) ON FAIT UNE PETITE PAUSE à chaque fois sinon ça va trop vite...
        rest(20);
    }

    return 0;
}
END_OF_MAIN();



/************************************************/
/*     DEFINITIONS DES SOUS-PROGRAMMES          */
/*  devront aller dans un autre .c : acteurs.c  */
/************************************************/


// Allouer et initialiser un acteur
t_acteur * creerActeur(int type, int x, int y, int dx, int tmpdx, int tmpimg)
{
    // pointeur sur l'acteur qui sera créé (et retourné)
    t_acteur *acteur;

    // Création (allocation)
    acteur = (t_acteur *)malloc(1*sizeof(t_acteur));

    // Initialisation géométrie et déplacement
    acteur->x=x;           acteur->y=y;
    acteur->dx=dx;
    acteur->tmpdx=tmpdx;
    acteur->cptdx=0;
    acteur->tx=tabSequences[type].tx;
    acteur->ty=tabSequences[type].ty;

    // Initialisation séquence d'images de l'animation
    acteur->imgcourante=0;
    acteur->tmpimg=tmpimg;
    acteur->cptimg=0;

    // numéro de séquence
    acteur->type=type;

    // on retourne cet acteur fraichement créé
    return acteur;
}

// Pour remplir un tableau avec des acteurs créés
// Sur cet exemple on crée 6 acteurs, chacun associé à une séquence
void remplirTabActeurs(t_acteur * tab[NACTEUR])
{
    // Appeler NACTEUR fois creerActeur avec les paramètres souhaités :
    //                (type,   x,   y,  dx, tmpdx, tmpimg )
    tab[0]=creerActeur(   0, 500,   0,  -5,     1,      5 );
    tab[1]=creerActeur(   1, 300, 400,   3,     1,      8 );
    tab[2]=creerActeur(   2, 300, 212,   2,     6,     20 );
    tab[3]=creerActeur(   3, 100, 122,  -3,     1,      8 );
    tab[4]=creerActeur(   4, 500,  70,   4,     1,      2 );
    tab[5]=creerActeur(   5, 350, 200,  -2,     1,      4 );
}


// Actualiser un acteur (bouger ...)
void actualiserActeur(t_acteur *acteur)
{

    // gestion des bords "à la pac man"
    // sur cet exemple seulement sur l'axe x (car pas de dy)
    if (acteur->x+acteur->tx < 0) acteur->x=SCREEN_W;
    if (acteur->x > SCREEN_W) acteur->x=-acteur->tx;

    // calculer nouvelle position
    // nouvelle position = position actuelle + deplacement seulement une fois sur tmpdx
    // sur cet exemple seulement sur l'axe x (car pas de dy)
    acteur->cptdx++;
    if (acteur->cptdx >= acteur->tmpdx){
        acteur->cptdx=0;
        acteur->x = acteur->x + acteur->dx;
    }

    // gestion enchainement des images
    // incrémenter imgcourante une fois sur tmpimg
    acteur->cptimg++;
    if (acteur->cptimg >= acteur->tmpimg){
        acteur->cptimg=0;
        acteur->imgcourante++;
        // quand l'indice de l'image courante arrive à nimg de la séquence
        // on recommence la séquence à partir de 0
        if ( acteur->imgcourante >= tabSequences[ acteur->type ].nimg )
            acteur->imgcourante=0;
    }
}

// Gérer l'évolution de l'ensemble des acteurs
void actualiserTabActeurs(t_acteur * tab[NACTEUR])
{
    int i;

    for (i=0;i<NACTEUR;i++)
        actualiserActeur(tab[i]);
}


// Dessiner un acteur sur une bitmap bmp
void dessinerActeur(BITMAP *bmp, t_acteur *acteur)
{
    // Pointeur sur la séquence concernée (prise en compte du type de l'acteur)
    t_sequence *seq;
    seq=&tabSequences[ acteur->type ];

    //  Prise en compte du numéro d'image courante de l'acteur dans cette séquence
    draw_sprite(bmp, seq->img[ acteur->imgcourante ], acteur->x, acteur->y);
}

// Dessiner l'ensemble des acteurs sur une bitmap bmp
void dessinerTabActeurs(BITMAP *bmp,t_acteur * tab[NACTEUR])
{
    int i;

    for (i=0;i<NACTEUR;i++)
        dessinerActeur(bmp,tab[i]);
}


// Charger les images d'une séquence d'animation
// Découpe une image source en plusieurs vignettes
// (doivent être rangées de gauche à droite et de haut en bas)
void chargerSequence(t_sequence * seq)
{
    BITMAP *source;  // la bitmap qui charge l'image de séquence (temporairement)
    int i;           // indice de l'image dans la séquence
    int ix,iy;       // indices (horizontal et vertical) dans le "tableau" des images
    int sx,sy;       // coordonnées correspondantes (en pixels)


    // Charger l'image de séquence
    source=load_bitmap(seq->nomSource,NULL);
    if (!source)
    {
        allegro_message("pas pu trouver %s", seq->nomSource);
        exit(EXIT_FAILURE);
    }

    // Allouer le tableau de pointeur sur les images de l'animation
    seq->img=(BITMAP **)malloc(seq->nimg*sizeof(BITMAP *));

    // Allouer les images de l'animation et les récupérer sur l'image source
    ix=0;
    iy=0;
    for (i=0;i<seq->nimg;i++)
    {
        // allouer image
        seq->img[i]=create_bitmap(seq->tx,seq->ty);

        // récupérer image
        sx=ix*seq->tx;
        sy=iy*seq->ty;
        blit(source,seq->img[i],sx,sy,0,0,seq->tx,seq->ty);

        // préparer indices pour l'image suivante
        ix++;          // colonne suivante
        if (ix >= seq->ncol)  // si je suis à droite de la dernière colonne alors...
        {
            ix=0;      // repartir sur la colonne 0
            iy++;      // à la ligne en dessous
        }
    }

    // On a fini de récupérer séparément chaque étape (image) de l'animation
    // on n'a donc plus besoin de l'image source qui les regroupe
    destroy_bitmap(source);
}

// Charger toutes les séquences du tableau global tabSequence
void chargerTabSequences()
{
    int i;

    for (i=0;i<NSEQUENCE;i++)
        chargerSequence(&tabSequences[i]);
}