Balls Demo

/*
  balls.c
  
  SDL Example
  A simple, pachinko-style game
  
  Bill Kendrick
  1/2000
*/


#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>
#include <mixer.h>


/* Constraints: */

#define MAX_BALLS 5
#define MAX_PEGS 32


/* Typedefs: */

typedef struct ball_type {
  int alive;
  int x, y;
  int xm, ym;
} ball_type;

typedef struct peg_type {
  int alive;
  int x, y;
} peg_type;


/* Global variables: */

ball_type balls[MAX_BALLS];
peg_type pegs[MAX_PEGS];


/* Local function prototypes: */

SDL_Surface * load_and_convert(char * file);
void addball(int x, int y);
void addpeg(int x, int y);
void clearballs(void);
void clearpegs(void);


/* --- MAIN --- */

int main(int argc, char * argv[])
{
  SDL_Event event;
  SDLKey key;
  SDL_Surface * screen, * ball, * peg;
  SDL_Rect dest;
  int i, j, done, paused;
  Uint32 last_time;
  Mix_Chunk * tink;
  
  
  /* Init SDL: */
  
  if (SDL_Init(SDL_INIT_AUDIO |
	       SDL_INIT_VIDEO) < 0)
    {
      fprintf(stderr, "Init: %s\n",
	      SDL_GetError());
      exit(1);
    }
  
  
  /* Open display: */
  
  screen = SDL_SetVideoMode(320, 240,
			    16, 0);
  
  if (screen == NULL)
    {
      fprintf(stderr, "Video: %s\n",
	      SDL_GetError());
      exit(1);
    }
  
  
  /* Set window title: */
  
  SDL_WM_SetCaption("SDL Demo: Balls",
		    "Balls");
  
  
  /* Load graphics: */
  
  ball = load_and_convert("ball.bmp");
  peg = load_and_convert("peg.bmp");
  
  
  /* Initialize Mixer: */
  
  if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY,
		    MIX_DEFAULT_FORMAT,
		    2, 512) < 0)
    {
      fprintf(stderr,
	      "Can't open audio: %s\n",
	      SDL_GetError());
      exit(1);
    }
  
  
  /* Load sounds: */
  
  tink = Mix_LoadWAV("tink.wav");
  
  if (tink == NULL)
    {
      fprintf(stderr,
	      "Can't open tink.wav: %s\n",
	      SDL_GetError());
      exit(1);
    }
  
  
  /* Initialize balls and pegs: */
  
  clearballs();
  clearpegs();
  
  
  /* --- MAIN LOOP: --- */
  
  done = 0;
  paused = 0;
  
  do
    {
      /* Get the time at the beginning
	 of this iteration: */
      
      last_time = SDL_GetTicks();
      
      
      /* Handle all queued events: */
      
      while (SDL_PollEvent(&event) > 0)
	{
	  if (event.type == SDL_QUIT)
	    {
	      /* Quit request - quit! */
	      
	      done = 1;
	    }
	  else if (event.type ==
		   SDL_KEYDOWN)
	    {
	      /* Keypress: */
	      
	      key = event.key.keysym.sym;
	      
	      if (key == SDLK_ESCAPE)
		{
		  /* Escape - quit! */
		  
		  done = 1;
		}
	      else if (key == SDLK_SPACE)
		{
		  /* Space - Un/pause! */
		  
		  paused = !paused;
		}
	      else if (key == SDLK_TAB)
		{
		  /* Tab - Clear pegs: */
		  
		  clearpegs();
		}
	    }
	  else if (event.type ==
		   SDL_MOUSEBUTTONDOWN)
	    {
	      if (event.button.button == 1)
		{
		  /* Left click! Add a ball! */
		  
		  addball(event.button.x,
			  event.button.y);
		}
              else
		{
		  /* Not left! Add a peg! */
		  
		  addpeg(event.button.x,
			 event.button.y);
		}
	    }
	}

      
      /* Move all balls: */
      
      if (!paused)
	{
	  for (i = 0; i < MAX_BALLS; i++)
	    {
	      if (balls[i].alive)
		{
		  /* Apply gravity: */
		  
		  balls[i].ym++;
		  
		  
		  /* Keep speeds in bounds: */
		  
		  if (balls[i].ym > 24)
		    balls[i].ym = 24;
		  else if (balls[i].ym < -24)
		    balls[i].ym = -24;

		  if (balls[i].xm > 24)
		    balls[i].xm = 24;
		  else if (balls[i].xm < -24)
		    balls[i].xm = -24;

		  
		  /* Move ball: */
		  
		  balls[i].x = (balls[i].x +
				balls[i].xm);
		  
		  balls[i].y = (balls[i].y +
				balls[i].ym);
		  
		  
		  /* If it dropped off the
		     screen, kill it: */
		  
		  if (balls[i].y >= 240)
		    balls[i].alive = 0;
		  
		  
		  /* See if it hit a peg: */
		  
		  for (j = 0;
		       j < MAX_PEGS;
		       j++)
		    {
		      if (pegs[j].alive &&
			  balls[i].x >=
			  pegs[j].x - 24 &&
			  balls[i].x <=
			  pegs[j].x + 40 &&
			  balls[i].y >=
			  pegs[j].y - 16 &&
			  balls[i].y <=
			  pegs[j].y + 24)
			{
			  /* Bounce! */
			  
			  balls[i].ym =
			    -abs(balls[i].ym - 1);
			  
			  balls[i].xm =
			    ((balls[i].x -
			      pegs[j].x) / 4) *
			    ((abs(balls[i].xm) + 4)
			    / 4);
			  
			  
			  /* Play sound: */
			  
                          if (abs(balls[i].ym) > 1)
			    Mix_PlayChannel(-1,
			                    tink,
                                            0);
			}
		    }
		}
	    }
	}
      
      
      /* Erase entire screen: */
      
      SDL_FillRect(screen, NULL,
		   SDL_MapRGB(screen->format,
			      255,
			      255,
			      255));
      
      
      /* Draw all balls: */
      
      for (i = 0; i < MAX_BALLS; i++)
	{
	  if (balls[i].alive)
	    {
	      dest.x = balls[i].x;
	      dest.y = balls[i].y;
	      dest.w = 32;
	      dest.h = 32;
	      
	      SDL_BlitSurface(ball,
			      NULL,
			      screen,
			      &dest);
	    }
	}


      /* Draw all pegs: */
      
      for (i = 0; i < MAX_PEGS; i++)
	{
	  if (pegs[i].alive)
	    {
	      dest.x = pegs[i].x;
	      dest.y = pegs[i].y;
	      dest.w = 32;
	      dest.h = 32;
	      
	      SDL_BlitSurface(peg,
			      NULL,
			      screen,
			      &dest);
	    }
	}
      
      
      /* Update the entire screen: */
      
      SDL_Flip(screen);
      
      
      /* Pause until it's time for the
	 next frame: */
      
      if (SDL_GetTicks() < last_time + 20)
	{
	  SDL_Delay(last_time + 20 -
		    SDL_GetTicks());
	}
    }
  while (done == 0);
  
  
  /* Close up and quit: */
  
  SDL_Quit();
  
  return(0);
}


/* Load and convert a bitmap: */

SDL_Surface * load_and_convert(char * file)
{
  SDL_Surface * temp, * surf;
  
  
  /* Load: */
  
  temp = SDL_LoadBMP(file);
  if (surf == NULL)
    {
      fprintf(stderr,
 	      "Can't load %s: %s\n",
	      file, SDL_GetError());
      exit(1);
    }
  
  
  /* Convert: */
  
  surf = SDL_DisplayFormat(temp);
  if (surf == NULL)
    {
      fprintf(stderr,
	      "Can't convert %s: %s\n",
	      file, SDL_GetError());
      exit(1);
    }


  /* Make 100% white transparent: */
  
  if (SDL_SetColorKey(surf, (SDL_SRCCOLORKEY),
		      SDL_MapRGB(surf->format,
				 255,
				 255,
				 255)) < 0)
    {
      fprintf(stderr,
	      "Can't set colorkey %s: %s\n",
	      file, SDL_GetError());
      exit(1);
    }
  
  
  /* Free: */
  
  SDL_FreeSurface(temp);
  
  
  /* Return: */
  
  return(surf);
}


/* Add a ball: */

void addball(int x, int y)
{
  int i, found;
  
  
  /* Find an empty slot in the array: */
  
  found = -1;
  
  for (i = 0;
       i < MAX_BALLS && found == -1;
       i++)
    {
      if (balls[i].alive == 0)
	found = i;
    }
  
  
  /* Fill in the slot: */
  
  if (found != -1)
    {
      balls[found].alive = 1;
      
      balls[found].x = x - 16;
      balls[found].y = y - 16;
      
      balls[found].xm = 0;
      balls[found].ym = 0;
    }
}


/* Add a peg: */

void addpeg(int x, int y)
{
  int i, found;
  
  
  /* Find an empty slot in the array: */
  
  found = -1;
  
  for (i = 0;
       i < MAX_PEGS && found == -1;
       i++)
    {
      if (pegs[i].alive == 0)
	found = i;
    }
  
  
  /* Fill in the slot: */
  
  if (found != -1)
    {
      pegs[found].alive = 1;
      
      pegs[found].x = x - 16;
      pegs[found].y = y - 16;
    }
}


/* Clear all balls: */

void clearballs(void)
{
  int i;
  
  for (i = 0; i < MAX_BALLS; i++)
    balls[i].alive = 0;
}


/* Clear all pegs: */

void clearpegs(void)
{
  int i;
  
  for (i = 0; i < MAX_PEGS; i++)
    pegs[i].alive = 0;
}