#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <mem.h>
#include <graphics.h>
#include <dos.h>

#define NBLEVEL 7	//nb de levels

union REGS sour;
int BallesRestantes;
long Score,Level;
long SerieBriques; //nb de briques touch‚es … la suite sans intervention de la raquette
char LevelName[16];

class Bal
{
 public:
  int on;
  int taille;
  int coord[2]; // en seiziŠmes
  int vit[2];   //
  void lance();
  void affiche(int color);
  void deplace();
  void testcoll();
};

class Raq
{
 public:
  int x,taille;
  void affiche();
  void efface();
};

struct Briq
{
  int couleur;
  int type;
};

class Briqs
{public:
  Briq brique[16][16];
  void affiche();
  void affichesousballe();
  void affiche(int i,int j);
  void loadlevel(long n);
  int briquesrestantes;
};

/************ fonction souris *****************/
void mouse_win (int,int,int,int);
void mouse_curs (int);
int mouse_bout (void);
void mouse_pos (int*,int*);
void mouse_set (int,int);
int gest_sour ();

void presentation();
void init_screen(int s);
void bouton(int x, int y, int on_off, char* caption);
void affiche_statut();
void NextLevelPage();
void GameOverPage();
void WinPage();
void QuelleBrique(int x,int y,int *i,int *j);
void WaitRetrace();
void HiScore();

Bal Balle;
Raq Raquette;
Briqs briques;
void main()
{
  int st;	//statut: bouton enfonc‚, quitter...
  int x,y;
  presentation();
  do{
    init_screen(Level==0?0:1); //Level==0 => c'est la premiŠre fois
    do{
      st=gest_sour();
    }while(st==0);
    if(st==1){	//commencer
      mouse_curs(0);
      BallesRestantes=5;
      Score=0;
      mouse_win(16+Raquette.taille,624-Raquette.taille,0,0);
      Level=1 ;
      do{		//pour chaque level
	briques.loadlevel(Level);
	NextLevelPage();
	briques.affiche();
	do{    //pour chaque balle utilis‚e
	  mouse_set(320,0);
	  Raquette.x=320;
	  Raquette.taille=20;
	  mouse_win(16+Raquette.taille,624-Raquette.taille,0,0);
	  SerieBriques=0;
	  affiche_statut();
	  do{}while(kbhit() || mouse_bout());
	  do{	//tant qu'on n'a pas encore commenc‚
	    Raquette.affiche();
	  }while(mouse_bout()==0);
	  //un clic et on commence
	  BallesRestantes--;
	  affiche_statut();
	  Balle.lance();
	  do{
	    Raquette.affiche();
	    Balle.deplace();
	  }while(Balle.on==1&&briques.briquesrestantes>0);
	  do{}while(mouse_bout()==1);
	  Raquette.efface();
	  if(briques.briquesrestantes==0)BallesRestantes++;
	}while(BallesRestantes!=0&&briques.briquesrestantes>0);
	Level++;
      }while(Level<=NBLEVEL&&BallesRestantes!=0);
      if(BallesRestantes==0)GameOverPage();
      else WinPage();
      HiScore();
      mouse_win(0,639,0,479);
      mouse_set(160,455);
      mouse_curs(1);
    }
  }while(st!=2);
  closegraph();
  system("mode co80");
}



void presentation()
{
  int gdriver = DETECT, gmode;
  initgraph(&gdriver,&gmode,"");  // init ecran
  if (graphresult() != grOk ) exit(1);
  mouse_curs(0);                              // init souris
//// page intro
  settextjustify(CENTER_TEXT,CENTER_TEXT);
  setcolor(WHITE);
  setbkcolor(BLUE);
  rectangle(0,0,getmaxx(),getmaxy());
  rectangle(1,1,getmaxx()-1,getmaxy()-1);
  setcolor(YELLOW);
  outtextxy(500,460,"by Doodle, (C)1998");
  settextstyle(TRIPLEX_FONT,HORIZ_DIR,14);
  setcolor(LIGHTRED);
  outtextxy(getmaxx()/2,getmaxy()/2-40,"Bricks");
  setcolor(WHITE);
  outtextxy(getmaxx()/2-1,getmaxy()/2-41,"Bricks");
  do{}while(!kbhit() && !mouse_bout());
  settextstyle(TRIPLEX_FONT,HORIZ_DIR,3);
  setcolor(LIGHTRED);
  outtextxy(320,400,"Loading...");
}





/************************** init_screen ***************************/
void init_screen(int i)
{
  mouse_curs(0);
  setbkcolor(BLACK);
  if(i==0){
    cleardevice();
    setfillstyle(LTSLASH_FILL,RED);
    bar(0,0,639,479);
  }
  mouse_win(0,639,0,479);  // fixe la fenetre pour la souris
  BallesRestantes=5;
  Score=0;
  Level=0; LevelName[0]='\x0';
//Titre et boutons
  if(i==0){
    setfillstyle(SOLID_FILL,BLUE);   bar(10,430,320,470);
    setcolor(GREEN);                 rectangle(10,430,320,470);
    setcolor(LIGHTCYAN);
    settextstyle(TRIPLEX_FONT,HORIZ_DIR,4);
    settextjustify(LEFT_TEXT,BOTTOM_TEXT);
    outtextxy(20,460,"Bricks");
    settextstyle(DEFAULT_FONT,HORIZ_DIR,1);
    settextjustify(CENTER_TEXT,CENTER_TEXT);
    bouton(150,440,0,"Start");
    bouton(235,440,0,"Quit");
  }
//Statut du jeu
  affiche_statut();
//Fenˆtre d'affichage du jeu
  if(i==0){
    setfillstyle(SOLID_FILL,BLACK);   bar(10,10,630,420);
    setcolor(BLUE);
    rectangle(10,10,630,420);
    rectangle(12,12,628,418);
    rectangle(14,14,626,416);
    rectangle(15,15,625,415);
  }
//// place la souris
  mouse_set(160,450);       // on place la souris a cet endroit
  mouse_curs(1);            // affichage du curseur
}




/*********************************** bouton *********************************/
void bouton(int x, int y, int on_off, char* caption)
{
  int i, j, tab[8] = { 0, 0, 76, 0, 76, 20, 0, 20 };
  for (i=0;i<=7;i+=2){
    tab[i]=tab[i]+x;
    tab[i+1]=tab[i+1]+y;
  }
  mouse_curs(2);           // on cache le curseur de la souris
  setcolor(BLACK);
  setfillstyle(SOLID_FILL,LIGHTGRAY);
  fillpoly(4,tab);
  setcolor(on_off?DARKGRAY:WHITE);
  line(tab[0],tab[1],tab[2]-1,tab[3]);
  line(tab[0],tab[1],tab[6],tab[7]-2);
  setcolor(on_off?WHITE:DARKGRAY);
  line(tab[6],tab[7]-1,tab[4],tab[5]-1);
  line(tab[4],tab[5]-1,tab[2],tab[3]+1);
  settextjustify(CENTER_TEXT,CENTER_TEXT);
  settextstyle(DEFAULT_FONT,HORIZ_DIR,1);
  setcolor(on_off?BLACK:WHITE);
  outtextxy((2*x+76)/2,(2*y+20)/2,caption);
  setcolor(on_off?WHITE:BLACK);
  outtextxy((2*x+76)/2,(2*y+20)/2+1,caption);
  mouse_curs(1);
  settextjustify(LEFT_TEXT,BOTTOM_TEXT);
}





//**************************affiche_statut**********************************
void affiche_statut()
{
  char *s;
  setfillstyle(SOLID_FILL,BLUE);   bar(330,430,630,470);
  setcolor(GREEN);				rectangle(330,430,630,470);
  setcolor(WHITE);
  setfillstyle(SOLID_FILL,YELLOW);
  for(int i=0;i<BallesRestantes;i++)
    fillellipse(342+12*i,440,3,3);

  ultoa(Score,s,10);
  settextstyle(DEFAULT_FONT,HORIZ_DIR,1);
  settextjustify(LEFT_TEXT,BOTTOM_TEXT);
  outtextxy(340,455,"Score:");
  outtextxy(390,455,s);
  
  ultoa(Level,s,10);
  outtextxy(340,465,"Level:");
  outtextxy(390,465,s);
  outtextxy(422,465,LevelName);
}


//**************************NextLevelPage()*********************************
void NextLevelPage()
{
  setfillstyle(SOLID_FILL,BLACK);   bar(16,16,624,414);
  settextstyle(TRIPLEX_FONT,HORIZ_DIR,5);
  settextjustify(CENTER_TEXT,CENTER_TEXT);
  setcolor(LIGHTRED);
  if(Level>1)outtextxy(320,70,"Level completed");
  setcolor(LIGHTBLUE);
  outtextxy(320,150,"Next level:");
  outtextxy(320,200,LevelName);
  setcolor(YELLOW);
  settextstyle(DEFAULT_FONT,HORIZ_DIR,1);
  outtextxy(320,400,"Press any key to continue...");
  do{}while(kbhit() || mouse_bout());
  do{}while(!kbhit() && !mouse_bout());
  setfillstyle(SOLID_FILL,BLACK);   bar(16,16,624,414);
}

//*****************************GameOverPage()***************************
void GameOverPage()
{
  setfillstyle(SOLID_FILL,BLACK);   bar(16,16,624,414);
  settextstyle(TRIPLEX_FONT,HORIZ_DIR,5);
  settextjustify(CENTER_TEXT,CENTER_TEXT);
  setcolor(LIGHTRED);
  outtextxy(320,215,"Game over");
  setcolor(YELLOW);
  settextstyle(DEFAULT_FONT,HORIZ_DIR,1);
  outtextxy(320,400,"Press any key to continue...");
  do{}while(kbhit() || mouse_bout());
  do{}while(!kbhit() && !mouse_bout());
}

//*************************WinPage()*************************************
void WinPage()
{
  setfillstyle(SOLID_FILL,BLACK);   bar(16,16,624,414);
  settextstyle(TRIPLEX_FONT,HORIZ_DIR,5);
  settextjustify(CENTER_TEXT,CENTER_TEXT);
  setcolor(LIGHTRED);
  outtextxy(320,215,"You won that stupid game!");
  setcolor(YELLOW);
  settextstyle(DEFAULT_FONT,HORIZ_DIR,1);
  outtextxy(320,400,"Press any key to continue...");
  do{}while(kbhit() || mouse_bout());
  do{}while(!kbhit() && !mouse_bout());
}




//***********************fonctions de la classe Bal************************
void Bal::lance()
{
  int x,y;
  on=1;
  taille=3;
  mouse_pos(&x,&y);
  coord[0]=x<<4;	coord[1]=(389-taille)<<4;
  vit[0]=100; 		vit[1]=-100;
  setcolor(BLACK);
  affiche(YELLOW);
}

void Bal::affiche(int coul)
{
  setfillstyle(SOLID_FILL,coul);
  fillellipse(coord[0]>>4,coord[1]>>4,taille,taille);
}

void Bal::deplace()
{
//   WaitRetrace();
  setcolor(BLACK);
  affiche(BLACK);
  testcoll();
  briques.affichesousballe();
  if(on){
    coord[0]+=vit[0];
    coord[1]+=vit[1];
    WaitRetrace();
    affiche(YELLOW);
  }
}

void Bal::testcoll()
{
  int d,i,j,ii,jj;
  //teste si la balle touche un bord de la fenˆtre
   //gauche et droite:
  if     ((coord[0]>>4)-taille+(vit[0]>>4)<=15) vit[0]= abs(vit[0]);
  else if((coord[0]>>4)+taille+(vit[0]>>4)>=624)vit[0]=-abs(vit[0]);
  //haut et bas (balle perdue):
  if     ((coord[1]>>4)-taille+(vit[1]>>4)<=15 )vit[1]= abs(vit[1]);
  else if((coord[1]>>4)+taille+(vit[1]>>4)>=414)on=0;
  //teste si la balle touche la raquette
  if((coord[1]>>4)+taille+(vit[1]>>4)>=390 ){
    d=(coord[0]>>4)-Raquette.x;
    if(abs(d)<Raquette.taille+taille){
      if(abs(d)<Raquette.taille-7)
	vit[1]=-abs(vit[1]);
      else{
	vit[0]+=(d<<1);
	vit[1]=-abs(vit[1])+(d<<1)*vit[0]/vit[1]/vit[1];
	//par dvpt limit‚, pour avoir une vitesse globalement constante
      }
      SerieBriques=0;
//        Score-=5;
    }
  }
  //teste si la balle touche une brique
  QuelleBrique(coord[0],coord[1],&i,&j);
  QuelleBrique(coord[0]+vit[0],coord[1]+vit[1],&ii,&jj);
  if(ii>=0&&ii<16&&jj>=0&&jj<16){
    //on arrive ds une zone o— il y a des briques
    if(briques.brique[ii][jj].type&0x000F){  //il y a une brique
      if(ii==i&&jj!=j){
	vit[0]=-vit[0];
	if(!(briques.brique[ii][jj].type&0x0008)){
	  //elle n'est pas incassable
	  briques.brique[ii][jj].type--;
	  //if(!(briques.brique[ii][jj].type&0x0007))
	  //elle vient d'ˆtre cass‚e
	  briques.briquesrestantes--;
	  SerieBriques++;
	  Score+=SerieBriques*10;
	  affiche_statut();
	}
      }
      else if(jj==j&&ii!=i){
	vit[1]=-vit[1];
	if(!(briques.brique[ii][jj].type&0x0008)){
	  briques.brique[ii][jj].type--;
	  //if(!(briques.brique[ii][jj].type&0x0007))
	  //elle vient d'ˆtre cass‚e
	  briques.briquesrestantes--;
	  SerieBriques++;
	  Score+=SerieBriques*10;
	  affiche_statut();
	}
      }
      else if(ii!=i&&jj!=j){
	vit[0]=-vit[0]-2;	vit[1]=-vit[1]+2;
	if(!(briques.brique[ii][jj].type&0x0008)){
	  briques.brique[ii][jj].type--;
	  //if(!(briques.brique[ii][jj].type&0x0007))
	  //elle vient d'ˆtre cass‚e
	  briques.briquesrestantes--;
	  SerieBriques++;
	  Score+=SerieBriques*10;
	  affiche_statut();
	}
      }
    }
  }
}





//***********************fonctions de la classe Raq*************************
void Raq::affiche()
{
  int y;
  WaitRetrace();
  setfillstyle(SOLID_FILL,BLACK);
  bar(x-taille,390,x+taille,395);
  mouse_pos(&x,&y);
  setfillstyle(SOLID_FILL,LIGHTGREEN);
  bar(x-taille,390,x+taille,395);
}

void Raq::efface()
{
  setfillstyle(SOLID_FILL,BLACK);
  bar(x-taille,390,x+taille,395);
}







//**********************fonctions de la classe Briqs*************************
void Briqs::affiche()
{  for(int i=0;i<16;i++)for(int j=0;j<16;j++)affiche(i,j);}

void Briqs::affichesousballe()
{
  int i,j,ii,jj;
  QuelleBrique(Balle.coord[0],Balle.coord[1],&i,&j);
  for(ii=i-1;ii<=i+1;ii++)
    for(jj=j-1;jj<=j+1;jj++)
      if(ii>=0 && ii<16 && jj>=0 && jj<16)affiche(ii,jj);
}

void Briqs::affiche(int i,int j)
{
  int x,y;
  x=17+38*j;	y=17+20*i;
  if(brique[i][j].type&0x000F){
    setfillstyle(SOLID_FILL,brique[i][j].couleur);
    bar(x,y,x+36,y+18);
    putpixel(x,y,0);	putpixel(x+1,y,0);	putpixel(x,y+1,0);
    y+=18;
    putpixel(x,y,0);	putpixel(x+1,y,0);	putpixel(x,y-1,0);
    x+=36;
    putpixel(x,y,0);	putpixel(x-1,y,0);	putpixel(x,y-1,0);
    y-=18;
    putpixel(x,y,0);	putpixel(x-1,y,0);	putpixel(x,y+1,0);
  }
  else{
    setfillstyle(SOLID_FILL,BLACK);
    bar(x,y,x+36,y+18);
  }
}


void Briqs::loadlevel(long n)
{
  char s[14],t[14],u[2];
  FILE *f;
  int i,j;
  ltoa(n,s,10);
  sprintf(t,"level%s.brk",s);
  f=fopen(t,"rb");
  fseek(f,0,SEEK_SET);
  fread(LevelName,1,16,f);
  fseek(f,16,SEEK_SET);
  briquesrestantes=0;
  for(i=0;i<16;i++)
    for(j=0;j<16;j++){
      fread(u,1,2,f);
      brique[i][j].couleur=(int)(u[0]);
      brique[i][j].type=(int)(u[1]);
      if((u[1])&0x08!=0)briquesrestantes++;
    }
  fclose(f);
}




//*******************************QuelleBrique******************************
//indique les coordonn‚es (i,j) de la brique ds laquelle est (x>>4,y>>4)
void QuelleBrique(int x,int y,int *i,int *j)
{  *i=((y>>4)-17)/20;   *j=((x>>4)-17)/38;}


//*********************************WaitRetrace()****************************
void WaitRetrace()
{
  asm mov dx,3DAh;
 l1:
  asm{
    in al,dx
    and al,08h
    jnz l1
  }
 l2:
  asm{
    in al,dx
    and al,08h
    jz  l2
  }
}


//**********************************HiScore()*****************************
void HiScore()
{
  struct{
    char name[24];
    long lev;
    long score;
  }hisc[10];
  int i,j,i0=10;
  char tmp[12],playername[24],c;
  FILE *f;
  if((f=fopen("hiscore.brk","rb"))==NULL){
    for(i=0;i<10;i++){
      hisc[i].name[0]='\x0';
      hisc[i].lev=0;
      hisc[i].score=0;
    }
  }
  else{
    for(i=0;i<10;i++){
      fread(hisc[i].name,1,24,f);
      fread(&(hisc[i].lev),4,1,f);
      fread(&(hisc[i].score),4,1,f);
    }
  }
  fclose(f);

  if(Score>=hisc[9].score){
    for(i=9;i>0 && Score>=hisc[i-1].score;i--)hisc[i]=hisc[i-1];
    i0=i;
    for(j=0;j<24;j++)hisc[i].name[j]='\x0';
    hisc[i].lev=Level-1;
    hisc[i].score=Score;
  }
  //affichage des hiscores
  setfillstyle(SOLID_FILL,BLACK);   bar(16,16,624,414);
  settextstyle(TRIPLEX_FONT,HORIZ_DIR,3);
  settextjustify(CENTER_TEXT,CENTER_TEXT);
  setcolor(LIGHTRED);
  outtextxy(320,50,"HiScore");
  settextstyle(DEFAULT_FONT,HORIZ_DIR,1);
  setcolor(LIGHTCYAN);
  outtextxy(70,100,"Rank");
  outtextxy(260,100,"Name");
  outtextxy(450,100,"Level");
  outtextxy(550,100,"Score");
  setcolor(WHITE);
  for(i=0;i<10;i++)if(hisc[i].lev!=0){
    if(i==i0)setcolor(YELLOW);
    itoa(i+1,tmp,10);
    outtextxy(70,130+25*i,tmp);
    outtextxy(260,130+25*i,hisc[i].name);
    ltoa(hisc[i].lev,tmp,10);
    outtextxy(450,130+25*i,tmp);
    ltoa(hisc[i].score,tmp,10);
    outtextxy(550,130+25*i,tmp);
    if(i==i0)setcolor(WHITE);
  }

  if(i0<10){		//si le joueur est entr‚ dans le topten
    //saisir le nom du joueur
    for(i=0;i<24;i++)playername[i]='\x0';
    setcolor(YELLOW);
    outtextxy(260,400,"Enter your name...");
    i=0;	//indice dans la chaŒne playername
    c='\x0';
    do{
      do{ c=getch(); }while((c<'\r' || c>'z')&&(c!='\b'));
      if(c!='\r'){
	setcolor(BLACK);
	outtextxy(260,130+25*i0,playername);
	if(c=='\b'){
	  if(i> 0){
	    playername[i-1]='\x0';
	    i--;
	  }
	}else{
	  if(i<23){
	    playername[i]=c;
	    i++;
	  }
	}
	setcolor(YELLOW);
	outtextxy(260,130+25*i0,playername);
      }
    }while(c!='\r');
    memcpy(hisc[i0].name,playername,24);

    //enregistrer les nouvelles donn‚es de hiscore
    f=fopen("hiscore.brk","wb");
    for(i=0;i<10;i++){
      fwrite(hisc[i].name,1,24,f);
      fwrite(&(hisc[i].lev),4,1,f);
      fwrite(&(hisc[i].score),4,1,f);
    }
    fclose(f);
    setcolor(BLACK);
    outtextxy(260,400,"Enter your name...");
  }
}



/*********************************** SOURIS *********************************/

void mouse_win(int minx,int maxx,int miny,int maxy)
{
  sour.x.ax=7;
  sour.x.cx=minx;
  sour.x.dx=maxx;
  int86(0x33,&sour,&sour);
  sour.x.ax=8;
  sour.x.cx=miny;
  sour.x.dx=maxy;
  int86(0x33,&sour,&sour);
}

void mouse_curs(int curseur)
{
  sour.x.ax=curseur;
  int86(0x33,&sour,&sour);
}

int mouse_bout()
{
  sour.x.ax=3;
  int86(0x33,&sour,&sour);
  return(sour.x.bx);
}

void mouse_pos(int *sourx,int *soury)
{
  sour.x.ax=3;
  int86(0x33,&sour,&sour);
  *sourx=sour.x.cx;
  *soury=sour.x.dx;
}

void mouse_set(int x,int y)
{
  sour.x.ax=4;
  sour.x.cx=x;
  sour.x.dx=y;
  int86(0x33,&sour,&sour);
}

int gest_sour ()
{
  int st=0;
  int x,y;
  if(mouse_bout()==1){
    do{
      mouse_pos(&x,&y);
      if(y>=440 && y<=460){
	if(x>=150 && x<=226){  //bouton nouveau
	  if(st!=1){
	    bouton(150,440,1,"Start");
	    bouton(235,440,0,"Quit");
	    st=1;
	  }
	}
	else if(x>=235 && x<=316){ //bouton quitter
	  if(st!=2){
	    bouton(150,440,0,"Start");
	    bouton(235,440,1,"Quit");
	    st=2;
	  }
	}
	else{
	  if(st!=0){
	    if(st==1)bouton(150,440,0,"Start");
	    else if(st==2)bouton(235,440,0,"Quit");
	    st=0;
	  }
	}
      }
      else{
	if(st!=0){
	  if(st==1)bouton(150,440,0,"Start");
	  else if(st==2)bouton(235,440,0,"Quit");
	  st=0;
	}
      }
    }while(mouse_bout()==1);
    //relƒcher les boutons
    if(st==1)bouton(150,440,0,"Start");
    else if(st==2)bouton(235,440,0,"Quit");
  }
  return st;
}
