
/*                  BOMBER INSTINCT               */


/* Copyright © 2000 by Louis Gesbert and TeraSoft */

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/*
 *  Contact me at : metagribouille@netcourrier.com.
 *
 *                    +----------------------+
 *        Web site :  |www.bomberinstinct.org|
 *                    +----------------------+
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <SDL/SDL.h>
#include <SDL/SDL_audio.h>
#include <SDL/SDL_thread.h>

#include "main.h"
#include "pouvoirs.h"
#include "niveaux.h"
#include "evenements.h"
#include "options.h"
#include "son.h"
#include "gamelogic.h"
#include "intro.h"


/***********************************/
/*-----D é c l a r a t i o n s-----*/
/***********************************/

/*--Variables globales de configuration--*/

/* des options */
Uint32 flags=SDL_HWSURFACE|SDL_HWPALETTE;
/* des fichiers */
char repertoire[256];
char prefixeniv[256], prefixeimg[256];
char prefixespr[256], prefixeson[256];

/*--Structures qui servent en continu dans le jeu--*/

Plateau pl[16][15]; /* Toutes les cases qui constituent le niveau */
Personnage perso[8]; /* Description de l'état des personnages */
int nperso=2; /* réglage du nb de joueurs ds le menu */
Coord rscase[2][260]; int nrscase[2]={0,0}; /* Les cases à redessiner */
Coord rstunnel[2][260]; int ntunnel[2]={0,0}; /* pareil, mais par-dessus le reste */
SDL_Rect rsrect[512]; int nrsrect=0; /* Parties de l'écran à mettre à jour */
int nrs=0; /* sur quel buffer est le double-buffering */
Anims * anims=NULL; /* Animations à l'écran */
Anims * animsfin=NULL; /* On garde un pointeur sur le dernier élément
			  pour que les animations plus récentes passent par-dessus */

/*--Trucs dont le programme a besoin de se souvenir--*/

char **spritliste; 
SDL_Surface * bande;
SDL_Surface * fondecran;
SDL_Surface * txtmodesjeu[NMODES];
int score[8];
int enpause=0;

/*--Entrées/Sorties--*/

SDL_Surface *ecran;
Uint8 *clavier;
SDL_Joystick **joystick;
int numjoys;

/*--Variables qui servent en cours de jeu--*/

int compteur;
int encore;

/*--Variables des autres trucs dont on doit se souvenir quelque temps--*/

int danslemenu;
int options;
int choisi=0;
Coord fauxperso[MAX_NPERSO];

const int dx[4]={ 0, 0,-1, 1};
/* Donne le déplacement en x à partir de la direction */
const int dy[4]={-1, 1, 0, 0};
/* Donne le déplacement en y à partir de la direction */


/********************************/
/*-------F o n c t i o n s------*/
/********************************/

/*--Initialiser SDL--*/
int init_sdl(void)
{
  Uint32 params;
  SDL_Surface *icone;
  char chemin[256], titre[256];
  int ok;

  params = avecson ? SDL_INIT_VIDEO|SDL_INIT_AUDIO : SDL_INIT_VIDEO;
  if (avecjoy) params |= SDL_INIT_JOYSTICK;
  
  if ( SDL_Init(params) < 0 ) {
    printf("Impossible d'initialiser SDL: %s\n",SDL_GetError());
    exit(1);
  }
  if (avecjoy) {
    numjoys=SDL_NumJoysticks();
    printf("%i joysticks détectés: support %s.\n",
	   numjoys, numjoys>0 ? "activé" : "désactivé"); }
  else {
    numjoys=0;
    printf("Support du joystick désactivé.\n"); }

  sprintf(titre, "Bomber Instinct - v%s", VERSION);
  SDL_WM_SetCaption(titre, "BomberInstinct");
  sprintf(chemin, "%s/icone.bmp", prefixeimg);
  icone=SDL_LoadBMP(chemin);
  SDL_SetColorKey(icone, SDL_SRCCOLORKEY, 0);
  SDL_WM_SetIcon(icone, NULL);
  SDL_FreeSurface(icone);


  if (dblebuf)
    flags |= SDL_DOUBLEBUF;
  params = pleinecran ? flags|SDL_FULLSCREEN : flags;

  ok=SDL_VideoModeOK(640, 480, 8, params);
  if (!ok)
    erreur("Le mode vidéo requis n'est pas disponible (640x480)", NULL);
#ifdef DEBUG
  else if (ok!=8) {
    printf("Impossible de passer en mode couleurs 8 bits.\n");
    printf("Le jeu tournera, mais en émulation donc plus lentement.\n"); }
#endif
  ecran = SDL_SetVideoMode(640, 480, 8, params);
  if ( ecran == NULL ) {
    printf("Impossible de choisir le mode 640*480 8 bits!\n%s\n", SDL_GetError());
    return 1;
  }
  if ((ecran->flags & SDL_DOUBLEBUF)==SDL_DOUBLEBUF) 
    {
      dblebuf=1;
      printf("Double buffering: activé.\n");
    }
  else if (dblebuf<2)
    {
      dblebuf=0;
      printf("Double buffering: désactivé.\n");
    }
  else dblebuf=1;

  atexit(SDL_Quit);
  atexit(SDL_CloseAudio);
  SDL_EventState(SDL_APPMOUSEFOCUS|SDL_MOUSEBUTTONDOWN|SDL_MOUSEBUTTONUP|
  		 SDL_MOUSEMOTION|SDL_SYSWMEVENT, SDL_IGNORE);
  SDL_EnableKeyRepeat(0,0);
  SDL_ShowCursor(0);
  return 0;
}


/*--Initialisation des joysticks--*/
void initjoy(void)
{
  int i;
  joystick=(SDL_Joystick **)calloc(sizeof(SDL_Joystick *), numjoys);
  for (i=0; i<numjoys; i++)
    joystick[i] = SDL_JoystickOpen(i);
}


/*--Quitte avec un message d'erreur--*/
void erreur(char *message, char *param)
{
  printf ("\nErreur :\n%s", message);
  if (param) printf(" : %s", param);
  printf (".\n%s\n", SDL_GetError());
  exit(0);
}


/*--Message d'information sur les options de démarrage--*/
void msg_info(void)
{
  printf("Bomber Instinct - version %s, by TeraSoft\n", VERSION);
  printf("Syntax : bi [+/-][esmjd]\n");
  printf("\t+\tactivates an attribute\n");
  printf("\t-\tdesactivates an attribute\n");
  printf("\te\tfullscreen mode\n");
  printf("\ts\tsound support\n");
  printf("\tm\tmusic support\n");
  printf("\tj\tjoystick support\n");
  printf("\td\tdouble-buffering (try it if the screen flickers)\n");
  exit(0);
}


/*--Tire un nb aléatoire entre 0 et (max-1)--*/
int aleat(int max)
{
  int num;
  num=(int)(max  *  ((float)rand()) / (RAND_MAX+1.0) );
  return num;
}


/*--Passe de la fenêtre au plein-écran et vice-versa (touche F1)--*/
void passenpleinecran()
{
  SDL_WM_ToggleFullScreen(ecran);

  if (pleinecran) pleinecran=0;
  else pleinecran=1;

  if ((ecran->flags & SDL_DOUBLEBUF)==SDL_DOUBLEBUF) 
    dblebuf=1;
  else
    dblebuf=0;

  return;
}


/*--Attend que le délai de boucle principale soit au moins VITESSE pour repartir--*/
int ajustevitesse(void)
{
  static int repart=0;
  int t;
#ifdef DEBUG
  int ecoule=repart ? SDL_GetTicks()-repart+VITESSE
    : VITESSE-1;
#endif
  do t=SDL_GetTicks();
  while (t<repart);
  repart=t+VITESSE;
#ifdef DEBUG
  return ecoule;
#else
  return 0;
#endif
}


/*--Met le jeu en pause et assombrit l'écran--*/
void pauseifie(void)
{
  SDL_Event evt;
  paletsg pal;

  while (SDL_PollEvent(&evt)); /* Vider la file d'évènements */

  pal=changepalette(0.8, 0.8, 0.8, 0, 0, 1, ecran->format->palette->ncolors, 10);
  SDL_PauseAudio(1);
    
  do
    SDL_WaitEvent(&evt);
  while (evt.type!=SDL_KEYDOWN ||
	 !(SDL_GetAppState()&SDL_APPACTIVE));
  restorepalette(pal, 50);
  SDL_PauseAudio(0);
  enpause=0;
  return;
}


/*--Affiche des informations, pose une question--*/
void dialogue(char * texte, int estunequestion)
{
  int nlignes=0;
  const char delim[]="#";
  SDL_Event evt;
  paletsg pal;
  char * lignes[256];
  SDL_Surface *surf;
  
  surf=SDL_CreateRGBSurface (0, 640, 480, 16, 0,0,0,0); 

  while (SDL_PollEvent(&evt)); /* Vider la file d'évènements */
  pal=changepalette(0.8, 0.8, 0.8, 0, 0, 1, ecran->format->palette->ncolors, 10);
  
  //  while (lignes[nlignes]=strtok(texte, &delim)) nlignes++;
  
  XCenteredString(surf, 100,"Bonjour\nComment ça va ?");

  SDL_BlitSurface(surf, NULL, ecran, NULL);

  rafraichit();

  do
    SDL_WaitEvent(&evt);
  while (evt.type!=SDL_KEYDOWN ||
	 !(SDL_GetAppState()&SDL_APPACTIVE));
  restorepalette(pal, 50);
  SDL_PauseAudio(0);
  enpause=0;
  return;
}



/*--Met en place des raccourcis-clavier--*/
int filtr_evt(const SDL_Event *evt)
{

  if (evt->type == SDL_QUIT)
    exit(0);
  if (evt->type == SDL_KEYDOWN)
    {
      if (evt->key.keysym.sym==SDLK_F10)
	exit(0);
      if (evt->key.keysym.sym==SDLK_F1)
	passenpleinecran();
      if (evt->key.keysym.sym==SDLK_PAUSE)
	enpause=1;
    }
  if (evt->type == SDL_ACTIVEEVENT)
    if (evt->active.type==SDL_APPACTIVE 
	&& !evt->active.gain)
      enpause=1;
  return 1;
}


/*--Affiche un écran noir--*/
void videcran(void)
{
  SDL_Rect tout={0, 0, 640, 480};
  SDL_FillRect(ecran, &tout, 0);
  SDL_Flip(ecran);
  if (dblebuf) {
    SDL_FillRect(ecran, &tout, 0);
    SDL_Flip(ecran); }
  return;
}


/*--Remet les structures utilisées pendant le jeu à zéro--*/
void reinit(void)
{
  int i;
  Anims * a;
  Evenement * e;
  while (anims) {
      a=anims->suiv; free(anims); anims=a; }
  while (evts[AVANT]) {
      e=evts[AVANT]->suiv; free(evts[AVANT]); evts[AVANT]=e; }
  while (evts[APRES]) {
      e=evts[APRES]->suiv; free(evts[APRES]); evts[APRES]=e; }

  for (i=0; i<NB_PERSO; i++)
    {
      Bombe * pt;
      while (perso[i].bombes) {
	pt=perso[i].bombes->suiv; free(perso[i].bombes); perso[i].bombes=pt; }
    }


  danslemenu=0;
  compteur=0;
  encore=1;
  nrsrect=0;
  nrscase[0]=0;
  nrscase[1]=0;
  nrs=0;
  ntunnel[0]=0;
  ntunnel[1]=0;
  decl=0;
  return;
}


/*--Règle les joysticks pour les joueurs--*/
void initjoy_persos()
{
  int i;
  for (i=0; i<8; i++)
    {
      perso[i].cmd.joy=NULL;
      if (perso[i].cmd.bas==0 || perso[i].cmd.gauche==0 ||
	  perso[i].cmd.droite==0 || perso[i].cmd.b1==0 ||
	  perso[i].cmd.b2==0)
	{
	  int njoy=perso[i].cmd.haut;
	  if (njoy>=numjoys || joystick[njoy]==NULL)
	    printf("Attention : Impossible d'initialiser le joystick %i demandé.\n", njoy);
	  else perso[i].cmd.joy=joystick[njoy];
	}
    }
  return;
}


/*--Initialise la structure perso avant chaque partie--*/
void initperso(int n)
{
  perso[n].dir=BAS;
  perso[n].anim=BAS;
  perso[n].mv=0;
  perso[n].statut=VIVANT;
  perso[n].animdroite=REPOS;
  perso[n].energie=100;
  perso[n].lflammes=2;
  perso[n].bombes=NULL;
  perso[n].pos.x=perso[n].pos.y=0;

  if (spritliste[n]) initpouv(n);
  return;
}


/*--Ajoute un rectangle à rafraichir--*/
void rsrectplus(int x, int y, int w, int h)
{
  rsrect[nrsrect].x=x;
  rsrect[nrsrect].y=y;
  rsrect[nrsrect].w=w;
  rsrect[nrsrect].h=h;
  nrsrect++;
  return;
}


/*--Ajoute une case du plateau à redessiner--*/
void rscaseplus(int x, int y)
{
  int i;
  if (pl[x][y].type==TUNNEL)
    {
      /* On vérifie que la case n'est pas déjà présente car
       * le blit est le truc le plus lent. */
      for (i=0; i<ntunnel[nrs]; i++)
	if (rstunnel[nrs][i].x==x && rstunnel[nrs][i].y==y)
	  return;
      if (ntunnel[nrs]<260) {
	rstunnel[nrs][ntunnel[nrs]].x=x;
	rstunnel[nrs][ntunnel[nrs]].y=y;
	ntunnel[nrs]++; }
    }
  else
    {
      /* Idem */
      for (i=0; i<nrscase[nrs]; i++)
	if (rscase[nrs][i].x==x && rscase[nrs][i].y==y)
	  return;
      if (nrscase[nrs]<260) {
	rscase[nrs][nrscase[nrs]].x=x;
	rscase[nrs][nrscase[nrs]].y=y;
	nrscase[nrs]++; }
    }
  x*=32;
  y*=32;
  if ((!dblebuf) && nrsrect<512)
    {
      for (i=0; i<nrsrect; i++)
	if (rsrect[i].x==x && rsrect[i].y==y)
	  return;
      /* Ça marche parce que tous les rsrect 
       * du plateau font au moins 32*32. */
      rsrect[nrsrect].x=x;
      rsrect[nrsrect].y=y;
      rsrect[nrsrect].w=
	rsrect[nrsrect].h=32;
      nrsrect++;
    }
  return;
}


/*--Ajoute une animation à la liste--*/
Anims *  animsplus(Sprite *sprite, int anim, int vitesse, int duree, int x, int y)
{
  Anims * nouv;
  nouv=(Anims *)malloc(sizeof(Anims));
  if (anims) anims->prec=nouv;
  else animsfin = nouv;
  nouv->suiv=anims;
  nouv->prec=NULL;
  nouv->pos.x=x;
  nouv->pos.y=y;
  nouv->anim=anim;
  nouv->frame=0;
  nouv->vitesse=vitesse;
  nouv->duree=duree;
  nouv->sprite=sprite;
  if (vitesse) nouv->compteur=0;
  else nouv->compteur=1;
  anims=nouv;
  return nouv;
}


/*--Supprime l'animation a (a!=NULL)--*/
void animsmoins(Anims * a)
{
  if (a==NULL) erreur("Erreur interne 4654132 : veuillez contacter le programmeur", NULL);
  rscaseplus(a->pos.x, a->pos.y);

  if (a->suiv) a->suiv->prec=a->prec;
  if (a->prec) a->prec->suiv=a->suiv;
  else {
    if (a->suiv==NULL) animsfin=NULL;
    anims = a->suiv; } /* a est le premier élément de anims. anims doit maintenant pointer sur le suivant */
  free(a);
  return;
}


/*--Redessine une case (x, y) du plateau--*/
void dessinecase(int ncases, Coord *co)
{
  int i;
  for (i=0; i<ncases; i++)
    {
      if ((danslemenu || !dblebuf) && nrsrect<512) {
	rsrect[nrsrect].x = danslemenu ? 32*(co[i].x+2) : 32*co[i].x;
	rsrect[nrsrect].y=32*co[i].y;
	rsrect[nrsrect].w=
	  rsrect[nrsrect].h=32;
	nrsrect++; }

      if (danslemenu) {
	SDL_Rect rectorig={rsrect[nrsrect-1].x-64, rsrect[nrsrect-1].y,
			   rsrect[nrsrect-1].w   , rsrect[nrsrect-1].h};
	SDL_Rect rectdest=rsrect[nrsrect-1];
	SDL_BlitSurface(fondecran, &rectorig,
			ecran, &rectdest); }
      else
	{
	  if (pl[co[i].x][co[i].y].sol)
	    blitsprit(sol, 0, 0, co[i].x*32, co[i].y*32);
	  blitsprit(pl[co[i].x][co[i].y].elem, pl[co[i].x][co[i].y].anim,
		    pl[co[i].x][co[i].y].frame, co[i].x*32, co[i].y*32);
	}
    }
  return;
}


/* A-t-on le droit de se rendre dans la case (x, y)
 * en allant dans la direction dir ? */
int partir(int x, int y, int dir)
{
  if (dir<4) {
    int px=x-dx[dir];
    int py=y-dy[dir];
    if (px<0 || 15<px || py<0 || 14<py) return 0;
    else if (pl[px][py].type==TUNNEL)
      {
	if (dir==BAS && 
	    (pl[px][py].frame==1 || pl[px][py].frame==4 ||
	     pl[px][py].frame==5 || pl[px][py].frame==9 )) return 0;
	else if (dir==HAUT &&
		 (pl[px][py].frame==0 || pl[px][py].frame==6 ||
		  pl[px][py].frame==7 || pl[px][py].frame==9 )) return 0;
	else if (dir==GAUCHE &&
		 (pl[px][py].frame==2 || pl[px][py].frame==5 ||
		  pl[px][py].frame==7 || pl[px][py].frame==8 )) return 0;
	else if (dir==DROITE &&
		 (pl[px][py].frame==3 || pl[px][py].frame==4 ||
		  pl[px][py].frame==6 || pl[px][py].frame==8 )) return 0;
      }
    else if (pl[px][py].type==SENSUNIQUE)
      {
	if (dir!=pl[px][py].anim) return 0;
      } 
  }
  return 1;
}


/*--La case (x,y) est-elle libre ?--*/
/* <=0 -> non libre */
/*   1 -> libre */
/*  -1 -> non libre mais destructible */
/*  -2 -> non libre à cause d'une bombe */
int libre(int x, int y, int dir)
{
  int ret=0;
  if (x<0 || 15<x || y<0 || 14<y)
    return 0;
  
  else if (!partir(x, y, dir)) return 0;
      
  else if (pl[x][y].type==LIBRE || pl[x][y].type==EVENEMENT
	   || pl[x][y].type==CONTINU || pl[x][y].type==BONUS)
    ret=1;

  else if (pl[x][y].type==DESTRUCTIBLE)
    return -1;
  
  else if (pl[x][y].type==SENSUNIQUE)
    {
      if (dir==pl[x][y].anim || dir==4)
	ret=1;
    }

  else if (pl[x][y].type==TUNNEL)
    {
      if (dir==4 || pl[x][y].frame==10) ret=1;
      else if (dir==BAS &&
	       (pl[x][y].frame!=0 && pl[x][y].frame!=6 &&
		pl[x][y].frame!=7 && pl[x][y].frame!=9 )) ret=1;
      else if (dir==HAUT && 
	       (pl[x][y].frame!=1 && pl[x][y].frame!=4 &&
		pl[x][y].frame!=5 && pl[x][y].frame!=9 )) ret=1;
      else if (dir==DROITE &&
	       (pl[x][y].frame!=2 && pl[x][y].frame!=5 &&
		pl[x][y].frame!=7 && pl[x][y].frame!=8 )) ret=1;
      else if (dir==GAUCHE &&
	       (pl[x][y].frame!=3 && pl[x][y].frame!=4 &&
		pl[x][y].frame!=6 && pl[x][y].frame!=8 )) ret=1;
    }

  if (ret==1 && pl[x][y].bombe==BOMBE)
    ret = -2;
  if (pl[x][y].bombe==EXPL)
    ret = 0;

  return ret;
}


/*--Le joueur n veut-il poser une bombe ?--*/
Bombe * posebombe(int x, int y, int n)
{
  Bombe * nouv;
  if (!(perso[n].statut & DETONATEUR || perso[n].energie>=NBOMBES) 
      || (pl[x][y].bombe)) // && libre(x, y, 4)>0
    return NULL;
  perso[n].energie-=NBOMBES;
  nouv=(Bombe *)malloc(sizeof(Bombe));
  if (perso[n].bombes) perso[n].bombes->prec=nouv;
  nouv->suiv=perso[n].bombes;
  nouv->prec=NULL;
  nouv->pos.x = x;
  nouv->pos.y = y;
  if (perso[n].statut & ETEINT)
    nouv->rebours = -1;
  else nouv->rebours = REBOURS;
  nouv->lflammes = perso[n].lflammes;
  nouv->car=0;
  
  if (perso[n].statut & DETONATEUR)
    {
      nouv->rebours = -1;
    }
  if (perso[n].statut & SUPERBOMBE)
    {
      nouv->car=SUPERBOMBE;
      perso[n].pouvactif[0]+=ACTIVATION;
    }
  perso[n].bombes=nouv;
  
  pl[x][y].bombe = BOMBE;
  return nouv;
}


/*--Supprime l'animation d'un bonus--*/
void bonusmoins(int x, int y)
{
  Anims * pt=anims;
  while (pt)
    {
      if (pt->sprite==bonus && pt->pos.x==x && pt->pos.y==y)
	{ animsmoins(pt); break; }
      pt=pt->suiv;
    }
  //animsplus destruction du bonus...
  return;
}

/*--Ramassage d'un bonus par un joueur--*/
void prendbonus(int n)
{
  int x, y;

  x=perso[n].pos.x;
  y=perso[n].pos.y;
  bruitsplus(s_bonus, 1);
  switch(pl[x][y].params[0])
    {
    case FLAMMESB: {
      perso[n].lflammes++;
    }break;
    case ENERGIEB: {
      perso[n].energie += (50+aleat(40));
      if (perso[n].energie>255)
	perso[n].energie=255;
    }break;
    case POUVOIRB: {
      pouvoirplus(n, 0, 0);
    }break;
    }
  pl[x][y].type=LIBRE;
  bonusmoins(x,y);

  return;
}


/*--Affichage d'un joueur à l'écran--*/
void afficheperso(int n, int frame)
{
  if (perso[n].statut&INVISIBLE)
    return;
  else if (perso[n].statut&DEGUISE)
    blitsprit(mur,2,0,perso[n].pixpos.x, perso[n].pixpos.y);
  else if (sprit[n]->anims[0]->nframes<8) /* Autrement dit, le personnage est une citrouille */
    blitsprit(sprit[n], 0, (compteur/2)%sprit[n]->anims[0]->nframes,
	      perso[n].pixpos.x,perso[n].pixpos.y);
  else
    blitsprit(sprit[n],perso[n].anim,frame,perso[n].pixpos.x,perso[n].pixpos.y);
  return;
}


/*--Anime les mouvements des joueurs--*/
void animouv(int n)
{
  int dirx, diry;
  int mv, frame=0;
  int x, y;
  x=perso[n].pos.x;
  y=perso[n].pos.y;
  perso[n].pixpos.x = x*32;
  perso[n].pixpos.y = y*32;

  dirx=dx[perso[n].dir]; diry=dy[perso[n].dir];
  mv=perso[n].mv;
  //if (perso[n].action!=RIEN || perso[n].action==INVINCIBLE)
  perso[n].anim=perso[n].dir;

  /* Si le personnage est déguisé, pour lui les propriétés */
  /* de sa case restent les mêmes même si elles changent pour */
  /* les autres. */
  if (perso[n].statut & DEGUISE) {
    pl[x][y].type=perso[n].pouvoirparam; }

  rscaseplus(x, y);
  
  if (mv>0)
    {
      perso[n].pixpos.x+=4*mv*dirx;
      perso[n].pixpos.y+=4*mv*diry;
      rscaseplus(x+dirx, y+diry);
      frame=mv;
    }
  if (mv==-1 || (perso[n].statut&RAPIDE && mv==-2) )
    {
      if (pl[x][y].type==EVENEMENT)
	evtsplus(pl[x][y].evt, n, x, y, 0, 1,
		 pl[x][y].nparams ? pl[x][y].params[0] : 0,
		 APRES, NULL);
      if (pl[x][y].type==BONUS)
	prendbonus(n);
    }

  if (mv<0)
    {
      mv++;
      /* mv est artificiellement minoré de 1 pour distinguer */
      /* quand le personnage arrive sur une case ou s'il y est déjà */
      perso[n].pixpos.x+=4*mv*dirx;
      perso[n].pixpos.y+=4*mv*diry;
      if (mv) {
	rscaseplus(x-dirx, y-diry);
	frame=8+mv; } /* (mv<=0) */
      else
	frame=0;
    }

  afficheperso(n, frame);

  /* Le personnage est déguisé, les autres le prennent donc pour un mur */
  if (perso[n].statut & DEGUISE) {
    perso[n].pouvoirparam=pl[x][y].type;
    pl[x][y].type=CAMOUFL; }

  if (perso[n].mv)
    {
      perso[n].mv++;
    }

  /* Les 4 premières frames de mouvement sont positives (1..4) */
  /* et les 4 dernières négatives (-5..-1 -> -4..-0) */
  /* Comme ça le personnage est considéré comme étant sur la case */
  /* dont il est le plus proche. */
  if (perso[n].mv==4)
    {
      if (perso[n].statut & DEGUISE)
	{
	  pl[x][y].type=perso[n].pouvoirparam;
	  perso[n].pouvoirparam=pl[x+dirx][y+diry].type;
	  pl[x+dirx][y+diry].type=CAMOUFL;
	}
      perso[n].pos.y+=diry;
      perso[n].pos.x+=dirx;
      perso[n].mv=-5;
    }
  
  if (perso[n].mv && perso[n].statut&RAPIDE)
    perso[n].mv++;

  return;
}


/*--Tentative d'assassinat du personnage "tue" par une bombe de "tueur"--*/
void mort(int tueur, int tue)
{
  int i, n_en_vie=0;

  if (perso[tue].statut&INVINCIBLE || !(perso[tue].statut&VIVANT))
    return;
  if (perso[tue].statut&DEGUISE)
    pl[perso[tue].pos.x][perso[tue].pos.y].type=perso[tue].pouvoirparam;

  if (modejeu==MODE_DEATHMATCH_GORE
      || (modejeu==MODE_DEATHMATCH && tueur!=tue))
    score[tueur]++;

  perso[tue].statut=MORT;
  perso[tue].animdroite=AGONIE;
  if (sprit[tue]->nanims>7)
    animsplus(sprit[tue], ANIM_MORT, 4, 1, perso[tue].pos.x, perso[tue].pos.y);
  bruitsplus(s_mort, 1);
  for (i=0; i<NB_PERSO; i++)
    if (perso[i].statut&VIVANT)
      {
	n_en_vie++;
      }
  if (n_en_vie<2)
    {
      evtsplus(termine, 0, 0, 0, 100, 1, 0, APRES, NULL);
    }
  return;
}


/*--Effet mortel sur la case (x,y), à cause du joueur n--*/
void tue(int x, int y, int n)
{
  int i;
  for (i=0; i<NB_PERSO; i++)
    if (perso[i].pos.x==x && perso[i].pos.y==y)
      mort(n, i);
}


/*--Explosion d'une bombe--*/
void explose(int n, Bombe * b)
{
  void propagflam(int, int, int, int, int);
  
  pl[b->pos.x][b->pos.y].bombe = EXPL;
  evtsplus(destruction, n, b->pos.x, b->pos.y, 0, 1, 0, AVANT, NULL);
  bruitsplus(s_expl, 1);
  
  if (b->suiv) b->suiv->prec=b->prec;
  if (b->prec) b->prec->suiv=b->suiv;
  else /* b est la première bombe, "bombes" pointe maintenant sur b->suiv */
    perso[n].bombes = b->suiv;

  tue(b->pos.x, b->pos.y, n);
  propagflam(n, b->pos.x, b->pos.y, b->lflammes, b->car==SUPERBOMBE);
  free(b);

  return;
}


/*--Détecte la bombe de la case (x,y) pour la faire exploser--*/
void explosebombe (int x, int y)
{
  int i;
  Bombe * pt;
  for (i=0; i<NB_PERSO; i++)
    {
      pt=perso[i].bombes;
      while (pt)
	{
	  if (pt->pos.x==x && pt->pos.y==y) {
	    pt->rebours=1;
	    return; }
	  pt=pt->suiv;
	}
    }
  return; /* Ne devrait servir à rien */
}


/*--Casse un mur destructible--*/
void detruitbloc(int x, int y)
{
  int param;
  pl[x][y].elem = sol;
  pl[x][y].anim = 0;
  pl[x][y].frame = 0;
  pl[x][y].type = LIBRE;
  pl[x][y].bombe = EXPL;
  evtsplus(destruction, 0, x, y, 0, 1, 0, AVANT, NULL);
  animsplus(bloc, 1, 2, 1, x, y);
  if (aleat(BONUSFREQ)==0)
    {
      param=aleat(10);
      pl[x][y].params=(int *)malloc(sizeof(int));
      pl[x][y].nparams=1;
      if (0<=param && param<5) pl[x][y].params[0]=FLAMMESB;
      else if (5<=param && param<9)  pl[x][y].params[0]=ENERGIEB;
      else if (9<=param && param<10) pl[x][y].params[0]=POUVOIRB;
      pl[x][y].type=BONUS;
      animsplus(bonus, pl[x][y].params[0], 2, 0, x, y);
    }
  return;
}


/*--Propagation des flammes des bombes--*/
void propagflam(int n, int ox, int oy, int lflammes, int sup)
{
  int i, j;
  int x, y;
  int type;
  int anim;
  int caselibre;
  
  for (i=0; i<4; i++)
    {
      for (j=1; j<=lflammes; j++)
	{
	  x=ox+dx[i]*j; y=oy+dy[i]*j;
	  if (x<0 || x>15 || y<0 || y>14)
	    break;
	  if (!partir(x, y, i)) break;

	  caselibre=libre(x, y, i);
	  type=pl[x][y].type;

	  /* Contrairement aux personnages,
	   * les flammes font peu de cas du déguisement */
	  if (pl[x][y].type==CAMOUFL)
	    caselibre=1;

	  if (caselibre==-2) {
	    /* S'il y a une bombe mais que ç'aurait été libre sinon */
	    tue(x, y, n);
	    explosebombe(x,y);
	    if (!sup) break; }
	  else if (!caselibre && pl[x][y].type==TUNNEL)
	    /* Il y a un tunnel qui empêche le passage de la flamme */
	    break;
	  else if (pl[x][y].bombe==EXPL) {
	    /* L'élément a été détruit à ce round */
	    tue(x, y, n);
	    if (!sup) break; }

	  else if (type==BONUS)
	    {
	      tue(x, y, n);
	      pl[x][y].type=LIBRE;
	      pl[x][y].bombe=EXPL;
	      evtsplus(destruction, n, x, y, 0, 1, 0, AVANT, NULL);
	      bonusmoins(x,y);

	      if (!sup) break; /* Les super-flammes continuent leur chemin */
	      else {
		anim = (j==lflammes) ? (i+3) : (i/2+1);
		animsplus(flammes, anim, 2, 1, x, y); }
	    }
	  else if (caselibre>0 && pl[x][y].elem!=mur) {
	    tue(x, y, n);
	    anim = (j==lflammes) ? (i+3) : (i/2+1);
	    animsplus(flammes, anim, 2, 1, x, y); }
	  else if (pl[x][y].elem==mur) {
	    int animbrule=pl[x][y].frame+1;
	    tue(x, y, n);
	    if (animbrule<mur->nanims && 
		mur->anims[animbrule]->nframes>1 && pl[x][y].anim==0)
	      {
		animsplus(mur, animbrule, 2, 1, x, y);
		pl[x][y].anim=animbrule;
		pl[x][y].frame=mur->anims[animbrule]->nframes-1;
	      }
	    break; }
	  else if (type==DESTRUCTIBLE) {
	    tue(x, y, n);
	    detruitbloc(x, y);
	    if (sup) {
	      anim = (j==lflammes) ? (i+3) : (i/2+1);
	      animsplus(flammes, anim, 2, 1, x, y); }
	    else break; }

	  else break;
	}
    }
  animsplus(bombe, 5, 2, 1, ox, oy);
  return;
}


/*--Rafraichir l'écran (à chaque frame), suivant le mode--*/
int rafraichit(void)
{
  if (dblebuf) {
    SDL_Flip(ecran);
    nrsrect=0;
    nrs=!nrs; }
  else {
    SDL_UpdateRects(ecran, nrsrect, rsrect);
    nrsrect=0;
    nrs=0; }

  return;
}


/*--Dessine la jauge d'énergie du joueur n à droite de l'écran--*/
void jauge(int n)
{
  SDL_Rect jauge;
  SDL_Rect energie;
  jauge.y=n*48+114;
  jauge.x=523;
  jauge.h=10;
  jauge.w=106;
  energie.y=n*48+116;
  energie.x=525;
  energie.h=6;
  energie.w=102;

  if (!(perso[n].statut&VIVANT))
    {
      SDL_FillRect(ecran, &jauge, 1);
      return;
    }

  if (perso[n].pouvactif[0])
    blitsprit(sprit[n], ICONES, 
	      2*((compteur/3)%2), 567, 48*n+80);    
  else if (perso[n].energie>=perso[n].pouv[0].energie)
    blitsprit(sprit[n], ICONES, 0, 567, 48*n+80);
  else blitsprit(sprit[n], ICONES, 2, 567, 48*n+80);


  if (perso[n].pouvactif[1])
    blitsprit(sprit[n], ICONES, 
	      1+2*((compteur/3)%2), 599, 48*n+80);
  else if (perso[n].energie>=perso[n].pouv[1].energie)
    blitsprit(sprit[n], ICONES, 1, 599, 48*n+80);
  else blitsprit(sprit[n], ICONES, 3, 599, 48*n+80);

  energie.w=perso[n].energie/2.5;
  SDL_FillRect(ecran, &jauge, 1);
  SDL_FillRect(ecran, &energie, 2);
  return;
}


/*--Dessine la bande de droite (jauges, etc.)--*/
void dessinebande(void)
{
  int i;
  SDL_Rect recdroite={512, 0, 128, 480};
  SDL_Rect recbande={512, 0, 128, 80};
  SDL_FillRect(ecran, &recdroite, 0);
  SDL_BlitSurface(bande, NULL, ecran, &recbande);

  rsrectplus(recdroite.x, recdroite.y,
	     recdroite.w, recdroite.h);

  for (i=0; i<NB_PERSO; i++)
    {
      int y=48*i+80;
      jauge(i);
      
      if (!(perso[i].statut&VIVANT))
	{
	  if (sprit[i]->nanims<=ANIM_MORT)
	    blitsprit(bombe, 7, (compteur/3)%4,
		      525, y);
	  else if (AGONIE <= perso[i].animdroite) {
	    blitsprit(sprit[i], ANIM_MORT, (perso[i].animdroite-AGONIE)/3, 525, y);
	    if ( (++perso[i].animdroite - AGONIE)/3 >= 
		 sprit[i]->anims[ANIM_MORT]->nframes) perso[i].animdroite=ANIM_MORT;
	  }
	  else
	    blitsprit(sprit[i], ANIM_MORT, 
		      sprit[i]->anims[ANIM_MORT]->nframes-1, 525, y);
	}

      else {
	if (perso[i].animdroite>=sprit[i]->nanims)
	  perso[i].animdroite=BAS;
	blitsprit(sprit[i], perso[i].animdroite, (compteur/4)
		  % sprit[i]->anims[perso[i].animdroite]->nframes,
		  525, y); }

      rsrect[nrsrect].x=523;
      rsrect[nrsrect].y=y;
      rsrect[nrsrect].w=106;
      rsrect[nrsrect].h=32+12;
      nrsrect++;
    }
}


/*--Annonce que tout devra être redessiné à la prochaine frame--*/
void rdtout(void)
{
  int x, y;
  for (y=0; y<15; y++)
    for (x=0; x<16; x++)
      {
	if (pl[x][y].type==TUNNEL)
	  {
	    rstunnel[nrs][ntunnel[nrs]].x=x;
	    rstunnel[nrs][ntunnel[nrs]].y=y;
	    ntunnel[nrs]++;
	  }
	else
	  {
	    rscase[nrs][nrscase[nrs]].x=x;
	    rscase[nrs][nrscase[nrs]].y=y;
	    nrscase[nrs]++;
	  }
      }
  return;
}


/*--Redessiner les cases de décor--*/
void rdplateau(int rien)
{
  nrscase[0]=0;
  nrscase[1]=0;
  ntunnel[0]=0;
  ntunnel[1]=0;
  nrsrect=0;

  rdtout();
  if (dblebuf) {
    nrs = !nrs;
    rdtout(); }

  return;
}


/*--Gère les bombes--*/
void anibombes()
{
  Bombelist * anibombes2(int n, Bombe * b);
  void anibombes3(int n, Bombelist * b);
  int i=0;
  Bombelist * imminent[NB_PERSO];
  while (i<NB_PERSO) { imminent[i]=anibombes2(i, perso[i].bombes); i++; }
  /* On met à jour les états de toutes les bombes */
  i=0;
  while (i<NB_PERSO) { anibombes3(i, imminent[i]); i++; }
  /* Puis on fait exploser celles qui en ont besoin */
  return;
}


Bombelist * anibombes2(int n, Bombe * b)
{
  int frame, anim;
  Bombe * suiv;
  if (b==NULL) return NULL;
  suiv=b->suiv;
  
  if (b->rebours)
    {
      if (b->rebours>0) {
	anim= (5 * (REBOURS - b->rebours)) / REBOURS;
	b->rebours--;
	frame=((REBOURS - b->rebours) /3)
	  % bombe->anims[anim]->nframes; }
      else {
	anim=7;
	frame=(compteur/3)%bombe->anims[anim]->nframes; }
	  
      rscaseplus(b->pos.x, b->pos.y);
      blitsprit(bombe, anim, frame, b->pos.x*32, b->pos.y*32);
      return anibombes2(n,suiv);
    }
  else
    {
      Bombelist * nouv=(Bombelist *)malloc(sizeof(Bombelist));
      nouv->contenu=b;
      nouv->suiv=anibombes2(n,suiv);
      return nouv;
    }
}


void anibombes3(int n, Bombelist * bl)
{
  Bombelist * suiv;
  if (bl==NULL) return;
  suiv=bl->suiv;
  explose (n,bl->contenu);
  free(bl);
  anibombes3(n,suiv);
  return;
}


/*--Gère la structures anims--*/
void anime(Anims * a)
{
  Anims * prec;
  /* On parcourt la liste des animations en ordre inverse pour que
     les plus récentes soient affichées au-dessus des plus anciennes */
  if (a==NULL) return;
  prec=a->prec;

  rscaseplus(a->pos.x, a->pos.y);
  blitsprit(a->sprite, a->anim, a->frame,
	    a->pos.x*32, a->pos.y*32);
  if (a->vitesse) a->compteur++;
  if (a->compteur==a->vitesse)
    {
      a->compteur=0;
      if (a->frame < a->sprite->anims[a->anim]->nframes-1)
	a->frame++;
      else
	{
	  a->frame=0;
	  if (a->duree)
	    {
	      a->duree--;
	      if (!a->duree) animsmoins(a);
	    }
	}
    }
  anime(prec);
  return;
}


/*--trie les volontés de déplacement des joueurs--*/
void mouvement(int n, int dir)
{
  int x, y, stat;
  x=perso[n].pos.x+dx[dir];
  y=perso[n].pos.y+dy[dir];

  if (perso[n].statut & DEGUISE)
    pl[perso[n].pos.x][perso[n].pos.y].type=perso[n].pouvoirparam;

  stat=libre(x, y, dir);
  if (stat>0)
    perso[n].mv=1;
  else if (perso[n].statut & FURIEDESTRUCTRICE && stat==-1)
    {
      detruitbloc(x, y);
      bruitsplus(s_expl, 1);
      perso[n].mv=1;
    }
  else if (perso[n].statut & PASSEMURAILLES
	   && 0<=perso[n].pos.x+dx[dir] && perso[n].pos.x+dx[dir]<16
	   && 0<=perso[n].pos.y+dy[dir] && perso[n].pos.y+dy[dir]<15)
    perso[n].mv=1;

  if (perso[n].statut & DEGUISE) {
    perso[n].pouvoirparam=pl[perso[n].pos.x][perso[n].pos.y].type;
    pl[perso[n].pos.x][perso[n].pos.y].type=CAMOUFL; }
  
  return;
}


/*--trie les commandes des joueurs et renvoie un bitmask--*/
int commande(int n)
{
  int cmd=0x0;

  perso[n].cmd.precedent=perso[n].cmd.actif;

  if (perso[n].joueurIA)
    return perso[n].cmd.actif=gamelogic_main(n);

  else if (perso[n].cmd.joy==NULL)
    {
      if (clavier[perso[n].cmd.haut]==SDL_PRESSED) cmd|=CMD_HAUT;
      if (clavier[perso[n].cmd.bas]==SDL_PRESSED) cmd|=CMD_BAS;
      if (clavier[perso[n].cmd.gauche]==SDL_PRESSED) cmd|=CMD_GAUCHE;
      if (clavier[perso[n].cmd.droite]==SDL_PRESSED) cmd|=CMD_DROITE;
      if (clavier[perso[n].cmd.b1]==SDL_PRESSED) cmd|=CMD_B1;
      if (clavier[perso[n].cmd.b2]==SDL_PRESSED) cmd|=CMD_B2;
    }
  else
    {
      int var=SDL_JoystickGetAxis(perso[n].cmd.joy, 1);
      if (var < -100) cmd|=CMD_HAUT;
      if (var > 100) cmd|=CMD_BAS;
      var=SDL_JoystickGetAxis(perso[n].cmd.joy, 0);
      if (var < -100) cmd|=CMD_GAUCHE;
      if (var > 100) cmd|=CMD_DROITE;
      if (SDL_JoystickGetButton(perso[n].cmd.joy, 0)) cmd|=CMD_B1;
      if (SDL_JoystickGetButton(perso[n].cmd.joy, 1)) cmd|=CMD_B2;
    }
  return perso[n].cmd.actif=cmd;
}


/*--Gère les commandes des joueurs--*/
void touches(int n)
{
  int p=0;
  int cmd=commande(n);
  if (!danslemenu)
    {
      if (cmd&CMD_B2)
	{
	  p=1;
	  if (cmd&CMD_GAUCHE)
	    pouvoirplus(n, 0, 1);
	  if (cmd&CMD_DROITE)
	    pouvoirplus(n, 1, 1);
	  if (cmd&CMD_HAUT)
	    pouvoirplus(n, 2, 1);
	  if (cmd&CMD_BAS)
	    pouvoirplus(n, 3, 1);
	}
    }
  
  if (p);
  else if (cmd&CMD_HAUT && !perso[n].mv) {
    perso[n].dir=HAUT;
    mouvement(n, HAUT);
  }
  else if (cmd&CMD_BAS && !perso[n].mv) {
    perso[n].dir=BAS;
    mouvement(n, BAS);
  }
  else if (cmd&CMD_GAUCHE && !perso[n].mv) {
    perso[n].dir=GAUCHE;
    mouvement(n, GAUCHE);
  }
  else if (cmd&CMD_DROITE && !perso[n].mv) {
    perso[n].dir=DROITE;
    mouvement(n, DROITE);
  }
  if (!danslemenu)
    if (cmd&CMD_B1)
      {
	if (perso[n].statut & DETONATEUR)
	  {
	    Bombe * pt = perso[n].bombes;
	    evtsplus(restorestatut, n, 0, 0, 6, 1, DETONATEUR, APRES, NULL);
	    while (pt) {
	      if (pt->rebours==-1) pt->rebours=0;
	      pt=pt->suiv; }
	  }
	else if(!(perso[n].statut&PASBOMBES))
	  posebombe(perso[n].pos.x, perso[n].pos.y, n);
      }
  return;
}


/*--Écran de menu des choix de personnages--*/
int choix(char *nom, char *image)
{
  int i;
  FILE *niveau;
  char chemin[256];
  SDL_Rect recfond;
  SDL_Color pal[255];

  danslemenu=2;
  compteur=0;
  nrsrect=0;
  nrscase[0]=0;
  nrscase[1]=0;
  nrs=0;
  ntunnel[0]=0;
  ntunnel[1]=0;
  options=0;
  encore=1;

  videcran();
  
  if (Mix_PlayingMusic) Mix_HaltMusic();
  //if (avecmusique) Mix_PlayMusic(muschoix, -1);

  for (i=0; i<NB_PERSO; i++)
    {
      sprintf(spritliste[i], "%i.spr", i+1);
    }
  sprintf(chemin, "%s/%s", prefixeniv, nom);
  niveau = fopen(chemin, "r");
  if (niveau==NULL)
    erreur("Impossible d'ouvrir le fichier menu", nom);
  decl=0;
  couleurs = charge_sprite("infos.spr", decl);
  sprintf(chemin, "%s/%s", prefixeimg, image);
  fondecran=SDL_LoadBMP(chemin);
  SDL_SetColors(ecran, fondecran->format->palette->colors, decl, fondecran->format->palette->ncolors);
  decl+=fondecran->format->palette->ncolors;

  charge_graphs(niveau);
  charge_sprites(sprit, spritliste, NB_PERSO);
  charge_sprites(fauxsprit, persoliste, nsprit);

  for (i=0; i<NB_PERSO; i++)
    initperso(i);
  chargeniv(niveau);
  fclose(niveau);

  recfond.x=recfond.y=0;
  recfond.w=512; recfond.h=480;

  /* Mettre en noir et blanc */
  for (i=1; i<256 && i<ecran->format->palette->ncolors; i++)
    {
      SDL_Color *colors=ecran->format->palette->colors;
      float moy=(colors[i].r+colors[i].g+colors[i].b)/3.0;
      moy=((moy-30-127)*0.68)+127;
      /* luminosité -20 contraste -40 */
      pal[i-1].r= moy>=225 ? 255 : moy+30;
      pal[i-1].g= moy;
      pal[i-1].b= moy<=30 ? 0 : moy-30;
    }
  SDL_SetColors(ecran, pal, 1, 256);

  rdplateau(0);

  do
    {
      ajustevitesse();

      if (compteur<65536) compteur++;
      else compteur=0;

      dessinecase(nrscase[nrs], rscase[nrs]);
      if (dblebuf)
	dessinecase(nrscase[!nrs], rscase[!nrs]);
      nrscase[nrs]=0;
      
      anime(animsfin);
      SDL_PumpEvents();
      clavier=SDL_GetKeyState(NULL);
      for (i=0; i<nsprit; i++)
	{
	  if (strcmp(persoliste[i], "citrouille.spr"))
	    blitsprit(fauxsprit[i], 1, 0, fauxperso[i].x*32, fauxperso[i].y*32);
	}
      for (i=0; i<NB_PERSO; i++)
	{
	  if (perso[i].statut&VIVANT)
	    {
	      touches(i);
	      animouv(i);
	    }
	}
      //dessinecase(ntunnel, rstunnel);
      //ntunnel=0;
      agit(evts[APRES], APRES);

      rafraichit();
    }
  while(encore && clavier[SDLK_ESCAPE]!=SDL_PRESSED && clavier[SDLK_F8]!=SDL_PRESSED);

  reinit();
  detruit_sprites();
  SDL_FreeSurface(fondecran);
  
  if (clavier[SDLK_ESCAPE]==SDL_PRESSED)
    {
      while(clavier[SDLK_ESCAPE]==SDL_PRESSED) {
	SDL_PumpEvents();
	clavier=SDL_GetKeyState(NULL); }
      return 1;
    }
  else if (clavier[SDLK_F8]==SDL_PRESSED)
    {
      while(clavier[SDLK_ESCAPE]==SDL_PRESSED || clavier[SDLK_F8]==SDL_PRESSED) {
	SDL_PumpEvents();
	clavier=SDL_GetKeyState(NULL); }
      return 2;
    }

  choisi=1;
  return 0;
}


/*--Écran de menu principal et options--*/
int menu(int principal, char *nom, char *image)
{
  int i;
  int preums;
  FILE *niveau;
  char chemin[256];
  SDL_Rect recmodejeu={225, 320, 341, 31};

  danslemenu=1;
  compteur=0;
  nrsrect=0;
  nrscase[0]=0;
  nrscase[1]=0;
  nrs=0;
  ntunnel[0]=0;
  ntunnel[1]=0;
  options=0;
  encore=1;
  for (i=0; i<8; i++)
    score[i]=0;
  nperso=NB_PERSO;

  videcran();

  decl=0;
  couleurs = charge_sprite("infos.spr", decl);
  sprintf(chemin, "%s/%s", prefixeniv, nom);
  niveau = fopen(chemin, "r");
  if (niveau==NULL)
    erreur("Impossible d'ouvrir le fichier menu", nom);

  sprintf(chemin, "%s/%s", prefixeimg, image);
  fondecran=SDL_LoadBMP(chemin);
  if (fondecran==NULL)
    erreur("Impossible de charger l'image", image);
  SDL_SetColors(ecran, fondecran->format->palette->colors, decl, fondecran->format->palette->ncolors);
  decl+=fondecran->format->palette->ncolors;
  if (!principal)
    {
      for (i=0; i<NMODES; i++)
	{
	  sprintf(chemin, "%s/mode_%i.bmp", prefixeimg, i);
	  txtmodesjeu[i]=SDL_LoadBMP(chemin);
	  if (txtmodesjeu[i]==NULL)
	    erreur("Impossible de charger l'image", image);
	}
    }

  charge_graphs(niveau);
  sprit[0]=charge_sprite("citrouille.spr", decl);
  for (i=0; i<NB_PERSO; i++)
    initperso(i);
  chargeniv(niveau);
  fclose(niveau);

  rdplateau(0);

  preums=1+dblebuf;
  
  do
    {
      ajustevitesse();

      if (compteur<65536) compteur++;
      else compteur=0;

      dessinecase(nrscase[nrs], rscase[nrs]);
      if (dblebuf)
	dessinecase(nrscase[!nrs], rscase[!nrs]);
      nrscase[nrs]=0;

      anime(animsfin);
      if (principal) blitsprit(couleurs, 1, 0, (3+nperso)*32, 9*32);

      agit(evts[AVANT], AVANT);

      SDL_PumpEvents();
      clavier=SDL_GetKeyState(NULL);
      touches(0);
      animouv(0);

      agit(evts[APRES], APRES);
      //dessinecase(ntunnel, rstunnel);
      //ntunnel=0;

      if (preums && !principal) { /* c-à-d menu des options */
	SDL_BlitSurface(txtmodesjeu[modejeu], NULL, ecran, &recmodejeu);
	rsrectplus(161, 320, 341, 31); }

      rafraichit();

      if (clavier[SDLK_ESCAPE]==SDL_PRESSED)
	exit(0);

      if (preums) preums--;
    }
  while(encore);

  reinit();
  detruit_sprites();
  SDL_FreeSurface(fondecran);
  if (!principal)
    for (i=0; i<NMODES; i++)
      SDL_FreeSurface(txtmodesjeu[i]);
  
  if (principal) NB_PERSO=nperso;
  if (options)
    {
      menu(0, "options.niv", choixlangue(chemin, "Options", "bmp"));
      return 1;
    }
  
  return 0;
}


/*--Évenements déclenchés dans les menus--*/
void menuevt(Evenement * e)
{
  int val, i, n_restant=0;
  int x=e->pos.x,
    y=e->pos.y,
    p=e->proprietaire,
    param=pl[x][y].params[0];

  if (param==SUIVANT)
    {
      encore=0;
      bruitsplus(s_teleport, 1);
    }
  else if (MINNB<param && param<MAXNB)
    {
      rdplateau(0);
      nperso=param-110;
      bruitsplus(s_bouton, 1);
    }
  else if (MINTOUCHES<param && param<MAXTOUCHES)
    touchescfg(x, y, param-221);
  else if (param==SAUVECONFIG)
    sauvecfg();
  else if (param==OPTIONS)
    {
      options=1;
      encore=0;
    }
  else if (param==MODEDEJEU || param==MODEDEJEUPLUS)
    {
      SDL_Rect recmodejeu={225, 320, 341, 31};
      if (param==MODEDEJEUPLUS)
	modejeu = NMODES -1;
      else
	modejeu = (modejeu+1) %(NMODES-1);
      SDL_BlitSurface(txtmodesjeu[modejeu], NULL, ecran, &recmodejeu);
      if (dblebuf) {
	SDL_Flip(ecran);
	SDL_BlitSurface(txtmodesjeu[modejeu], NULL, ecran, &recmodejeu);
	SDL_Flip(ecran); }
      else rsrectplus(161, 320, 341, 31);
      bruitsplus(s_bouton, 1);
    }      
  else if (param==QUITTER)
    exit(0);
  else if (MINPERSO<param && param<MAXPERSO)
    {
      val=param-121;
      if (val<nsprit)
	if (strcmp(spritliste[p],"citrouille.spr"))
	  {
	    strcpy(spritliste[p], persoliste[val]);
	    sprit[p]=fauxsprit[val];
	  }
    }
  else if (param==CESTPARTI)
    {
      perso[p].statut=MORT;
      for (i=0; i<NB_PERSO; i++)
	if (perso[i].statut&VIVANT)
	  {
	    n_restant++;
	    e->proprietaire=i; /* ??? */
	  }
      if (!n_restant)
	{
	  encore=0;
	  if (Mix_PlayingMusic) Mix_HaltMusic();
	}
      else bruitsplus(s_teleport, 1);
    }
  return;
}


/*--Boucle principale du jeu--*/
int jeu(void)
{
  paletsg pal;
  int preums=1;
#ifdef DEBUG
  int i;
  Uint32 stat[10000];
  int nstat=0;
  int nral=0;
  float moy=0;
#endif

  if (!dblebuf)
    pal=changepalette(0.0, 0.0, 0.0, 1, 256, 256, 256, 0); /* tout noir */

  rdplateau(0);

  bruitsplus(s_debut, 1);
  do
    {
      int i=0;

#ifdef DEBUG
      if (nstat<10000) stat[nstat++]=ajustevitesse();
      else ajustevitesse();
#else
      ajustevitesse();
#endif

      if (compteur<65536) compteur++;
      else compteur=0;

      dessinecase(nrscase[nrs], rscase[nrs]);
      if (dblebuf)
	dessinecase(nrscase[!nrs], rscase[!nrs]);
      nrscase[nrs]=0;
      
      SDL_PumpEvents();
      clavier=SDL_GetKeyState(NULL);

      agit(evts[AVANT], AVANT);
      anime(animsfin);
      anibombes();
      while (i<NB_PERSO)
	{
	  if (perso[i].statut&VIVANT)
	    {
	      if (compteur%REGEN == 0)
		if (perso[i].energie<255) perso[i].energie++;
	      touches(i);
	      animouv(i);
	    }
	  i++;
	}
      agit(evts[APRES], APRES);


      dessinecase(ntunnel[nrs], rstunnel[nrs]);
      if (dblebuf)
	dessinecase(ntunnel[!nrs], rstunnel[!nrs]);
      ntunnel[nrs]=0;
      
      gerepouvoirs();
      dessinebande();

      rafraichit();

      if (preums) {
	if (!dblebuf)
	  restorepalette(pal, 60);
	preums=0; }

      if (enpause==1) {
	pauseifie(); }
    }
  while(encore && clavier[SDLK_ESCAPE]!=SDL_PRESSED && clavier[SDLK_F8]!=SDL_PRESSED);
  if (Mix_PlayingMusic) Mix_HaltMusic();

#ifdef DEBUG
  /*--Faisons un peu de statistiques sur les performances--*/
  for (i=0; i<nstat; i++) {
    moy+=stat[i];
    if (stat[i]>VITESSE)
      nral++; }
  moy /= (float)nstat;
  printf("Statistiques sur %i rafraichissements d'écran:\n", nstat);
  printf("%.2f frames par seconde en moyenne, ", 1000/moy);
  printf("limitées par le maximum de %i fps\n", 1000/VITESSE);
  printf("%i frame%s(%.2f%%) étai%st au-delà du maximum.\n",
	 nral, nral>1 ? "s " : " ", (100.0*nral)/nstat, nral>1 ? "en" : "");
#endif

  reinit();
  SDL_FreeSurface(bande);

  //  if (!encore) bruitsplus(s_fin, 1);

  if (clavier[SDLK_F8]==SDL_PRESSED)
    {
      while(clavier[SDLK_F8]==SDL_PRESSED) {
	SDL_PumpEvents();
	clavier=SDL_GetKeyState(NULL); }
      detruit_sprites();
      return 2;
    }
  else if (clavier[SDLK_ESCAPE]==SDL_PRESSED)
    {
      while(clavier[SDLK_ESCAPE]==SDL_PRESSED) {
	SDL_PumpEvents();
	clavier=SDL_GetKeyState(NULL); }
      return 0;
    }
  else return 1;
}


/*--Initialisation de chaque partie--*/
int initpartie(void)
{
  int i;
  FILE *niveau;
  char chemin[256];
  char nomniveau[256];

  enpause=0;

  if (!choisi)
    {
      int ret;
      do ret=choix("choix.niv", "Choix.bmp");
      while (ret==2);
      if (ret==1) return 0;
    }

  i=aleat(nb_niveaux);
  strcpy(nomniveau, niveaux[i]);
  //printf("C'est parti : niveau %s [%i/%i].\n", nomniveau, i, nb_niveaux);

  videcran();

  reinit();
  detruit_sprites();

  couleurs = charge_sprite("infos.spr", decl);
  sprintf(chemin, "%s/%s", prefixeniv, nomniveau);
  niveau = fopen(chemin, "r");
  if (niveau==NULL)
    erreur("Impossible d'ouvrir le niveau", nomniveau);

  
  sprintf(chemin, "%s/Bande.bmp", prefixeimg);
  bande=SDL_LoadBMP(chemin);
  if (bande==NULL)
    erreur("Impossible de charger l'image", chemin);
  SDL_SetColors(ecran, bande->format->palette->colors, decl, bande->format->palette->ncolors);
  decl+=bande->format->palette->ncolors;

  charge_graphs(niveau);
  charge_sprites(sprit, spritliste, NB_PERSO);
  for (i=0; i<NB_PERSO; i++)
    initperso(i);
  chargeniv(niveau);
  fclose(niveau);

  dessinebande();

  if (Mix_PlayingMusic) Mix_HaltMusic();
  //if (avecmusique) Mix_PlayMusic(muschoix, -1);

  return jeu();
}


/*--Fin d'un round, affichage du score...--*/
int fin(int ret)
{
  int matchfini=0;
  int i, j; 
  int a, r, p, n=-1;
  char texte[8][20], chemin[256], nom[256];
  SDL_Rect g;
  Sprite *gagnant;
  g.w=g.h=32;
  g.x=(640-32)/2; g.y=(480-40*NB_PERSO)/2;

  videcran();

  if (changepersos) choisi=0;

  if (!ret)
    {
      return 1;
    }
  
  for (i=0; i<NB_PERSO; i++)
    if (perso[i].statut&VIVANT) n=i;
  if (n==-1) { gagnant=bombe; a=6; r=1; p=0;}
  else {
    gagnant=sprit[n];
    a = GAGNE>=sprit[n]->nanims ? BAS : GAGNE;
    r=3; p=1;
    if (modejeu==MODE_STD)
      score[n]++;
  }
  
  for (i=0; i<NB_PERSO; i++)
    if (score[i]>=16)
      {
	for (j=0; j<NB_PERSO;j++)
	  if (score[j]>score[i]-15)
	    score[j]-=score[i]-15;
	  else score[j]=0;
      }

  for (i=0; i<NB_PERSO; i++)
    {
      int ord=440-40*(NB_PERSO-i-1);
      blitsprit(sprit[i], BAS, 0, 64, ord);
      for (j=0; j<score[i]; j++)
	blitsprit(bombe, 0, 0, 112+j*32, ord);
    }
  SDL_Flip(ecran);
  if (dblebuf) {
    for (i=NB_PERSO-1; i>=0; i--)
      {
	int ord=440-40*(NB_PERSO-i-1);
	blitsprit(sprit[i], BAS, 0, 64, ord);
	for (j=0; j<score[i]; j++)
	  blitsprit(bombe, 0, 0, 112+j*32, ord);
      }
    SDL_Flip(ecran); }
  for (i=0; i<gagnant->anims[a]->nframes*r+p; i++)
    {
      SDL_PumpEvents();
      SDL_FillRect(ecran, &g, 0);
      if (strcmp(gagnant->nom,"Magus")) /* Le gagnant n'est pas Magus */
	blitsprit(gagnant, a, i%gagnant->anims[a]->nframes,
		  g.x, g.y);
      else
	{ /* C'est un bricolage pour l'anim de Magus */
	  int u=i/2;
	  if (u>=14) u = (u < 22) ? (6 + u%2) : (u - 8);
	  if (u < gagnant->anims[a]->nframes)
	    blitsprit(gagnant, a, u, g.x, g.y);
	}
      SDL_Flip(ecran);
      SDL_Delay(70);
    }

  SDL_Delay(500);

  /* Match fini ? */
  for (i=0, n=0; i<NB_PERSO; i++)
    if (score[i]>score[n]) n=i;
  
  if (score[n]>=NROUNDS)
    {
      matchfini=1;
      for (i=0; i<NB_PERSO; i++)
	if (score[i]>score[n]-1-pointdavance && i!=n)
	  matchfini=0;
      if (matchfini)
	{
	  videcran();
	  sprintf(nom, "%s", sprit[n]->nom);
	  nom[0]=toupper(nom[0]);
	  //	  printf("Gagnant joueur %i, personnage %s.\n", n+1, sprit[n]->nom);
	  printf("Gagnant joueur %i, personnage %s.\n", n+1, nom);
	  //	  strcpy(texte[0], sprit[n]->nom);
	  strcpy(texte[0], nom);
	  for (j=1; j<7; j++) strcpy(texte[j],"\0");
	  sprintf(texte[7], "Wins !!!");
	  //	  sprintf(chemin, "%s.bmp", sprit[n]->nom);
	  sprintf(chemin, "%s.bmp", nom);
	  ecrantexte(texte, chemin, 8, 0);
	}
    }
  
  detruit_sprites();

  clavier=SDL_GetKeyState(NULL);
  if (matchfini)
    return 1;
  if (clavier[SDLK_SPACE]==SDL_PRESSED) {
    while(clavier[SDLK_SPACE]==SDL_PRESSED) {
      SDL_PumpEvents(); clavier=SDL_GetKeyState(NULL); }    
    return 2; }
  else if (clavier[SDLK_ESCAPE]==SDL_PRESSED) {
    while(clavier[SDLK_ESCAPE]==SDL_PRESSED) {
      SDL_PumpEvents(); clavier=SDL_GetKeyState(NULL); }
    return 1; }
  return  2;
}


int main(int argc, char **argv)
{
  int i;
  int ret;
  int canal;
  char textecr[8][20], chemin[256];

  chargeconfig();

  sprintf(prefixeniv, "%s/Niveaux", repertoire);
  sprintf(prefixeimg, "%s/Decors", repertoire);
  sprintf(prefixespr, "%s/Sprites", repertoire);
  sprintf(prefixeson, "%s/Bruitages", repertoire);
  VITESSE=1000/VITESSE;

  spritliste=(char **)calloc(8, sizeof(char *));
  for (i=0; i<8; i++)
    spritliste[i]=(char *)calloc(256, sizeof(char));

  for (i=1; i<argc; i++)
    {
      if (argv[i][0]=='-')
	{
	  if (strchr(argv[i], 'e'))
	    pleinecran=0;
	  if (strchr(argv[i], 's'))
	    avecson=0;
	  if (strchr(argv[i], 'm'))
	    avecmusique=0;
	  if (strchr(argv[i], 'd'))
	    dblebuf=0;
	  if (strchr(argv[i], 'j'))
	    avecjoy=0;
	  if (strchr(argv[i], 'h'))
	    msg_info();
	}
      else if (argv[i][0]=='+')
	{
	  if (strchr(argv[i], 'e'))
	    pleinecran=1;
	  if (strchr(argv[i], 's'))
	    avecson=1;
	  if (strchr(argv[i], 'm'))
	    avecmusique=1;
	  if (strchr(argv[i], 'd'))
	    dblebuf=2;
	  if (strchr(argv[i], 'j'))
	    avecjoy=1;
	}
      else msg_info();
    }

  init_sdl();
  if (numjoys) initjoy();
  
  srand((unsigned int)SDL_GetTicks());
  SDL_SetEventFilter(filtr_evt);
  printf("Salut !\n");

  initson();
  chargebruitages();

  intro();
  bruitsplus(s_expl, 1);
  canal=bruitsplus(s_ecrantit, 0);
  if (numjoys) initjoy_persos();
  sprintf(textecr[0], "Bomber");
  sprintf(textecr[1], "Instinct");
  ecrantexte( textecr, "Titre.bmp", 2, 1);
  Mix_HaltChannel(canal);
  bruitsplus(s_grosrire,1);

  do
    {
      if (Mix_PlayingMusic) Mix_HaltMusic();

      while (menu(1, "menu.niv", choixlangue(chemin, "Menu", "bmp")));

      choisi=0;
      do
	{
	  do ret=initpartie();
	  while (ret==2);
	}
      while (fin(ret)==2);
    }
  while (1);
  
  exit(0);
}
/*--C'est fini !--*/
