/**************************************************************************
  CADRE GENERAL DE GESTION DE LISTES D'ACTEURS EN QUANTITE VARIABLE :
  les structures acteurs sont créées et détruites
  selon les besoins du jeu, des sous-programmes gèrent
  automatiquement l'ajout et le retrait d'acteur du terrain de jeu

  Sur cet exemple les acteurs sont des DISQUES
  qui apparaissent au centre et disparaissent sur les bords
  Appuyer sur entrée pour ajouter un disque par itération
  Appuyer sur espace pour visualiser les emplacements utilisés
  dans le tableau de pointeur de la structure t_listeActeurs

  Le même principe général s'applique
  à des acteurs plus complexes, personnages interactifs, animés...
**************************************************************************/

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


/**********************/
/*     STRUCTURES     */
/**********************/

// chaque acteur qui se déplace
typedef struct acteur
{

    // coordonnée (du coin sup. gauche)
    int x, y;

    // vecteur deplacement
    int dx, dy;

    // tailles (rayons des disques)
    int rayon;

    // couleur des disques
    int couleur;

    // vivant :
    //   0 mort (doit disparaitre de la liste)
    //   1 vivant
    int vivant;

} t_acteur;

// Une collection de acteurs
typedef struct listeActeurs
{
    // nombre maxi d'éléments
    // =taille du tableau de pointeurs
    int max;

    // nombre effectif de pointeurs utilisés
    // (les autres sont à NULL)
    int n;

    // le tableau de pointeurs (alloué dynamiquement)
    t_acteur **tab;

} t_listeActeurs;



/*********************/
/*     PROTOTYPES    */
/*********************/

// Allouer et initialiser un acteur
//   ( à adapter selon besoins )
t_acteur * creerActeur();

// Allouer et initialiser une liste (vide) de acteurs
t_listeActeurs * creerListeActeurs(int maxacteurs);

// Retourne un booléen vrai si il reste de la place
// dans la liste, faux sinon
int libreListeActeurs(t_listeActeurs *la);

// Allouer et ajouter un acteur à la liste
// et retourner l'adresse de ce nouveau acteur
// retourne NULL en cas de problème
t_acteur * ajouterActeur(t_listeActeurs *la);

// Enlever et libèrer un acteur qui était dans la liste en indice i
void enleverActeur(t_listeActeurs *la,int i);


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

// Gérer l'évolution de l'ensemble des acteurs
void actualiserListeActeurs(t_listeActeurs *la);


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

// Dessiner sur une bitmap l'ensemble des acteurs
void dessinerListeActeurs(BITMAP *bmp,t_listeActeurs *la);


// Dessiner sur une bitmap les cases utilisées par des acteurs
// ( pour visualiser: pas utile dans un programme finalisé )
void dessinerCasesListeActeurs(BITMAP *bmp,t_listeActeurs *la);


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

int main()
{

    // Buffer
    BITMAP *page;

    // La collection des acteurs
    t_listeActeurs *acteurs;

    // Proba de création de disque (en %)
    int probaNouveau=15;

    // Il y aura 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,800,600,0,0)!=0)
    {
        allegro_message("prb gfx mode");
        allegro_exit();
        exit(EXIT_FAILURE);
    }

    // buffer
    page=create_bitmap(SCREEN_W,SCREEN_H);

    // préparer la liste des acteurs (100 maxi)
    // mais vide initialement
    acteurs=creerListeActeurs(100);

    // BOUCLE DE JEU
    while (!key[KEY_ESC])
    {
        // effacer buffer
        clear_bitmap(page);

        // ajouter du monde
        // de manière probabiliste (par défaut)
        // ou systématiquement si touche entrée
        if (rand()%100 < probaNouveau || key[KEY_ENTER])
            ajouterActeur(acteurs);

        // bouger tout le monde
        actualiserListeActeurs(acteurs);

        // afficher tout le monde
        dessinerListeActeurs(page,acteurs);

        // optionnel (pour visualiser ce qui se passe)
        if (key[KEY_SPACE])
            dessinerCasesListeActeurs(page,acteurs);

        // afficher tout ça à l'écran
        blit(page,screen,0,0,0,0,SCREEN_W,SCREEN_H);

        // petite temporisation
        rest(10);
    }

    return 0;
}
END_OF_MAIN();



/************************************************/
/*     DEFINITIONS DES SOUS-PROGRAMMES          */
/************************************************/


// Allouer et initialiser un acteur
t_acteur * creerActeur(){
    t_acteur *nouv;

    // Allouer
    nouv=(t_acteur *)malloc(1*sizeof(t_acteur));

    // Initialiser ...

    nouv->rayon=rand()%20+20;

    nouv->x=SCREEN_W/2-nouv->rayon/2;
    nouv->y=SCREEN_H/2-nouv->rayon/2;

    do {
        nouv->dx=rand()%11-5;
        nouv->dy=rand()%11-5;
    } while (nouv->dx==0 && nouv->dy==0);

    nouv->couleur=makecol(rand()%128+128,rand()%128+128,rand()%128+128);

    nouv->vivant=1;

    // Retourner ce nouveau acteur tout frais
    return nouv;
}

// Actualiser un acteur (bouger, sortie écran ...)
void actualiserActeur(t_acteur *acteur){

    // deplacement
    acteur->x=acteur->x+acteur->dx;
    acteur->y=acteur->y+acteur->dy;

    // si dépasse un bord alors disparait
    if (acteur->x+2*acteur->rayon<0 || acteur->x>SCREEN_W || acteur->y+2*acteur->rayon<0 || acteur->y>SCREEN_H )
        acteur->vivant=0;

}

// Dessiner un acteur sur la bitmap bmp
void dessinerActeur(BITMAP *bmp,t_acteur *acteur){
    circlefill(bmp,acteur->x+acteur->rayon,acteur->y+acteur->rayon,acteur->rayon,acteur->couleur);
}

// Allouer et initialiser une liste (vide) de acteurs
t_listeActeurs * creerListeActeurs(int maxacteurs){
    t_listeActeurs *nouv;
    int i;

    nouv=(t_listeActeurs *)malloc(1*sizeof(t_listeActeurs));

    nouv->max=maxacteurs;
    nouv->n=0;
    nouv->tab=(t_acteur **)malloc(maxacteurs*sizeof(t_acteur*));

    for (i=0;i<maxacteurs;i++)
        nouv->tab[i]=NULL;

    return nouv;
}

// Retourne un booléen vrai si il reste de la place
// dans la liste, faux sinon
int libreListeActeurs(t_listeActeurs *la){
    return la->n < la->max;
}

// Allouer et ajouter un acteur à la liste
// et retourner l'adresse de ce nouveau acteur
// retourne NULL en cas de problème
// ( mais il vaut mieux d'abord vérifier qu'il
//   y a de la place disponible avant d'appeler )
t_acteur * ajouterActeur(t_listeActeurs *la){
    int i;
    t_acteur *acteur;

    // Liste pleine, on alloue rien et on retourne NULL...
    if (la->n >= la->max)
        return NULL;

    // Allouer un acteur initialisé
    acteur=creerActeur();

    // Chercher un emplacement libre
    i=0;
    while (la->tab[i]!=NULL && i<la->max)
        i++;

    // Si on a trouvé ...
    // (normalement oui car on a vérifié n<max)
    if (i<la->max){
        // Accrocher le acteur à l'emplacement trouvé
        la->tab[i]=acteur;
        la->n++;
    }
    // Sinon c'est qu'il y a un problème de cohérence
    else
        allegro_message("Anomalie gestion ajouterActeur : liste corrompue");

    return acteur;
}

// Enlever et libèrer un acteur qui était dans la liste en indice i
void enleverActeur(t_listeActeurs *la,int i){

    // Vérifier qu'il y a bien un acteur accroché en indice i
    if (la->tab[i]!=NULL)
    {
        // libérer la mémoire du acteur
        free(la->tab[i]);

        // marquer l'emplacement comme libre
        la->tab[i]=NULL;
        la->n--;

    }
}

// Gérer l'évolution de l'ensemble des acteurs
void actualiserListeActeurs(t_listeActeurs *la){
    int i;

    // actualiser chaque acteur
    // si un acteur n'est plus vivant, l'enlever de la liste
    for (i=0;i<la->max;i++)
        if (la->tab[i]!=NULL){
            actualiserActeur(la->tab[i]);
            if (!la->tab[i]->vivant){
                enleverActeur(la,i);
            }
        }
}

// Dessiner sur une bitmap l'ensemble des acteurs
void dessinerListeActeurs(BITMAP *bmp,t_listeActeurs *la){
    int i;

    for (i=0;i<la->max;i++)
        if (la->tab[i]!=NULL)
            dessinerActeur(bmp,la->tab[i]);

}


// Dessiner sur une bitmap les cases utilisées par des acteurs
// ( pour visualiser: pas utile dans un programme finalisé )
void dessinerCasesListeActeurs(BITMAP *bmp,t_listeActeurs *la){
    int i;

    for (i=0;i<la->max;i++){
        textprintf_ex(bmp,font,8*i,0,makecol(255,255,255),0,"%d",i/10);
        textprintf_ex(bmp,font,8*i,8,makecol(255,255,255),0,"%d",i%10);
        if (la->tab[i]!=NULL)
            rectfill(bmp,8*i,16,8*(i+1),32,makecol(255,0,0));
        else
            rectfill(bmp,8*i,16,8*(i+1),32,makecol(0,255,0));
    }
}