/*
  vectoroids.c

  An asteroid shooting game with vector graphics.
  Based on "Agendaroids."
  
  by Bill Kendrick
  bill@newbreedsoftware.com
  http://www.newbreedsoftware.com/vectoroids/
  
  November 30, 2001 - April 20, 2002

  Vectoroids DC by Williams Sams
  A port of Bill Kendrick's 'Vectoroids' to the Sega Dreamcast
  http://www.storm-studios.net/vectoroidsdc
  May 25 - June 3 2k3  : Would have been done faster if I knew I thew some
  errors in in on the 25th :-/
*/
#include <kos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_mixer.h>
#include "vectoroids.h"
#include "storm.h"

/* Data: */
enum {
  SND_BULLET,
  SND_AST1,
  SND_AST2,
  SND_AST3,
  SND_AST4,
  SND_THRUST,
  SND_EXPLODE,
  SND_GAMEOVER,
  SND_EXTRALIFE,
  NUM_SOUNDS
};

char * sound_names[NUM_SOUNDS] = {
  DATA_PREFIX "sounds/bullet.wav",
  DATA_PREFIX "sounds/ast1.wav",
  DATA_PREFIX "sounds/ast2.wav",
  DATA_PREFIX "sounds/ast3.wav",
  DATA_PREFIX "sounds/ast4.wav",
  DATA_PREFIX "sounds/thrust.wav",
  DATA_PREFIX "sounds/explode.wav",
  DATA_PREFIX "sounds/gameover.wav",
  DATA_PREFIX "sounds/extralife.wav"
};

char * mus_game_name = DATA_PREFIX "music/decision.s3m";

/* Globals: */
KOS_INIT_FLAGS(INIT_DEFAULT);
KOS_INIT_ROMDISK(romdisk);

SDL_Surface * screen, * bkgd;
Mix_Chunk * sounds[NUM_SOUNDS];
Mix_Music * game_music;
bullet_type bullets[NUM_BULLETS];
asteroid_type asteroids[NUM_ASTEROIDS];
bit_type bits[NUM_BITS];
int use_sound, use_joystick, fullscreen, text_zoom;
char zoom_str[24];
int x, y, xm, ym, angle;
int player_alive, player_die_timer;
int lives, score, high, level, game_pending, last;

/* Trig junk:  (thanks to Atari BASIC for this) */
int trig[12] = {
  1024,
  1014,
  984,
  935,
  868,
  784,
  685,
  572,
  448,
  316,
  117,
  0
};

/* Characters: */
int char_vectors[36][5][4] = {
  {
    /* 0 */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { 1, 2, 0, 2 },
    { 0, 2, 0, 0 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* 1 */
    { 1, 0, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },

  {
    /* 2 */
    { 1, 0, 0, 0 },
    { 1, 0, 1, 1 },
    { 0, 1, 1, 1 },
    { 0, 1, 0, 2 },
    { 1, 2, 0, 2 },
  },

  {
    /* 3 */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { 0, 1, 1, 1 },
    { 0, 2, 1, 2 },
    { -1, -1, -1, -1 }
  },

  {
    /* 4 */
    { 1, 0, 1, 2 },
    { 0, 0, 0, 1 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },

  {
    /* 5 */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 1 },
    { 0, 1, 1, 1 },
    { 1, 1, 1, 2 },
    { 1, 2, 0, 2 }
  },

  {
    /* 6 */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { 1, 2, 1, 1 },
    { 1, 1, 0, 1 }
  },

  {
    /* 7 */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },

  {
    /* 8 */
    { 0, 0, 1, 0 },
    { 0, 0, 0, 2 },
    { 1, 0, 1, 2 },
    { 0, 2, 1, 2 },
    { 0, 1, 1, 1 }
  },

  {
    /* 9 */
    { 1, 0, 1, 2 },
    { 0, 0, 1, 0 },
    { 0, 0, 0, 1 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 }
  },

  {
    /* A */
    { 0, 2, 0, 1 },
    { 0, 1, 1, 0 },
    { 1, 0, 1, 2 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* B */
    { 0, 2, 0, 0 },
    { 0, 0, 1, 0 },
    { 1, 0, 0, 1 },
    { 0, 1, 1, 2 },
    { 1, 2, 0, 2 }
  },

  {
    /* C */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* D */
    { 0, 0, 1, 1 },
    { 1, 1, 0, 2 },
    { 0, 2, 0, 0 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* E */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 }
  },

  {
    /* F */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 2 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }    
  },
  
  {
    /* G */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { 1, 2, 1, 1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* H */
    { 0, 0, 0, 2 },
    { 1, 0, 1, 2 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* I */
    { 1, 0, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* J */
    { 1, 0, 1, 2 },
    { 1, 2, 0, 2 },
    { 0, 2, 0, 1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* K */
    { 0, 0, 0, 2 },
    { 1, 0, 0, 1 },
    { 0, 1, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* L */
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* M */
    { 0, 0, 0, 2 },
    { 1, 0, 1, 2 },
    { 0, 0, 1, 1 },
    { 0, 1, 1, 0 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* N */
    { 0, 2, 0, 0 },
    { 0, 0, 1, 2 },
    { 1, 2, 1, 0 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* O */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { 1, 2, 0, 2 },
    { 0, 2, 0, 0 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* P */
    { 0, 2, 0, 0 },
    { 0, 0, 1, 0 },
    { 1, 0, 1, 1 },
    { 1, 1, 0, 1 },
    { -1, -1, -1, -1 }
  },
  
  { 
    /* Q */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { 1, 2, 0, 2 },
    { 0, 2, 0, 0 },
    { 0, 1, 1, 2 }
  },

  {
    /* R */
    { 0, 2, 0, 0 },
    { 0, 0, 1, 0 },
    { 1, 0, 1, 1 },
    { 1, 1, 0, 1 },
    { 0, 1, 1, 2 }
  },
  
  {
    /* S */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 1 },
    { 0, 1, 1, 1 },
    { 1, 1, 1, 2 },
    { 1, 2, 0, 2 }
  },

  {
    /* T */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* U */
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { 1, 2, 1, 0 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* V */
    { 0, 0, 0, 1 },
    { 0, 1, 1, 2 },
    { 1, 2, 1, 0 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* W */
    { 0, 0, 0, 2 },
    { 1, 0, 1, 2 },
    { 0, 1, 1, 2 },
    { 0, 2, 1, 1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* X */
    { 0, 0, 1, 2 },
    { 0, 2, 1, 0 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* Y */
    { 0, 0, 1, 1 },
    { 1, 0, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* Z */
    { 0, 0, 1, 0 },
    { 1, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  }
};

/* --- MAIN --- */
int main(int argc, char * argv[])
{
  int done;
  SDL_ShowCursor (SDL_DISABLE);

  setup();
  vmu_lcd_update();
  storm_data();
  do_intro();

  /* Set defaults: */
  score = 0;
  high = 0;
  game_pending = 0;

  /* Load scores from VMU: */
  load_vdc(&score, &high);

  /* Main app loop! */
  do
  {
    done = title();

    last=score;

    if (!done)
    {
      done = game();
    }
  }
  while (!done);

  save_vdc( score, high);
  finish();
  return 0;
}
/* Title screen: */
int title(void)
{
  int done, quit;
  int counter;
  Uint32 now_time, last_time;
  
  //Using KOS 2.X new Maple code
  maple_device_t *cond;
  cont_state_t *cont_state;

  counter = 0; 
  done = 0;
  quit = 0;

  SDL_BlitSurface(title_vdc, NULL, screen, NULL);
  fade_screen( FADE_IN, 1000 );
  SDL_UpdateRect(screen, 0, 0, 640, 480);
  Mix_PlayMusic(game_music, -1);

  do
  {
    last_time = SDL_GetTicks();
    counter++;

	cond = maple_enum_type(0, MAPLE_FUNC_CONTROLLER);
	if(cond == NULL){
      return -1;
   	}
   	
   	cont_state = (cont_state_t *) maple_dev_status(cond);
	if(!cont_state){
		return -1;
	}

    if  (((cont_state->buttons & CONT_START))||((cont_state->buttons & CONT_Y))||
    ((cont_state->buttons & CONT_DPAD_UP))||((cont_state->buttons & CONT_DPAD_DOWN))||
    ((cont_state->buttons & CONT_DPAD_LEFT))||((cont_state->buttons & CONT_DPAD_RIGHT)))
	{

    if ((cont_state->buttons & CONT_Y))
       { 

        quit=1;
        done=1;
        }

	if ((cont_state->buttons & CONT_START))
	{
      quit=0;
      done = 1;
	}
  }
  SDL_Flip(screen);
    
    now_time = SDL_GetTicks();
    
    if (now_time < last_time + (1000 / FPS))
      {
	SDL_Delay(last_time + 1000 / FPS - now_time);
      }
  }
  while(!done);
  return(quit);
}

/* --- GAME --- */
int game(void)
{
  int done, quit, counter;
  int i, j;
  int num_asteroids_alive;
  int left_pressed, right_pressed, up_pressed, shift_pressed;
  char str[10];
  Uint32 now_time, last_time;

  done = 0;
  quit = 0;
  counter = 0;
  left_pressed = 0;
  right_pressed = 0;
  up_pressed = 0;
  shift_pressed = 0;

  if (game_pending == 0)
  {  
    lives = 3;
    score = 0;
    player_alive = 1;
    player_die_timer = 0;
    angle = 90;
    x = (WIDTH / 2) << 4;
    y = (HEIGHT / 2) << 4;
    xm = 0;
    ym = 0;
    level = 1;
    reset_level();
  }

  game_pending = 1;
  
  /* Hide mouse cursor: */
  if (fullscreen)
    SDL_ShowCursor(0);
  
  /* Play music: */
      if (use_sound)
	{
	  if (!Mix_PlayingMusic())
	    Mix_PlayMusic(game_music, -1);
	}

  do
    {
      last_time = SDL_GetTicks();
      counter++;

      //Using KOS 2.X new Maple code
 	  maple_device_t *cond;
      cont_state_t *cont_state;
      
      cond = maple_enum_type(0, MAPLE_FUNC_CONTROLLER);
	  if(cond == NULL){
       return -1;
      }
   	
   	  cont_state = (cont_state_t *) maple_dev_status(cond);
	  if(!cont_state){
		return -1;
	  }
      
      /* Handle events: */
    if  (((cont_state->buttons & CONT_START))||((cont_state->buttons & CONT_A))||((cont_state->buttons & CONT_DPAD_UP))||
    ((cont_state->buttons & CONT_DPAD_DOWN))||((cont_state->buttons & CONT_DPAD_LEFT))||((cont_state->buttons & CONT_DPAD_RIGHT)))
	{
           	  /* Key press... */
		  if ((cont_state->buttons & CONT_DPAD_RIGHT))
		    {
		      /* Rotate CW */
  		      left_pressed = 0;
		      right_pressed = 1;
		    }
		  if ((cont_state->buttons & CONT_DPAD_LEFT))
		    {
		      /* Rotate CCW */
		      left_pressed = 1;
		      right_pressed = 0;
		    }
		  if ((cont_state->buttons & CONT_DPAD_UP))
		    {
		      /* Thrust! */
		      up_pressed = 1;
		    }
		  if (((cont_state->buttons & CONT_A)) &&
		           player_alive)
		    {
		      /* Fire a bullet! */
		      add_bullet(x >> 4, y >> 4, angle, xm, ym);
		    }
		  
		  if (((cont_state->buttons & CONT_X))||((cont_state->buttons & CONT_Y)))
		    {
		      /* Respawn now (if applicable) */
		      shift_pressed = 1;
		    }
  	    }
      
      /* Rotate ship: */
      if (right_pressed)
	{
	  angle = angle - 8;
	  if (angle < 0)
	    angle = angle + 360;

      right_pressed = 0;
	}
      else if (left_pressed)
	{
	  angle = angle + 8;
	  if (angle >= 360)
	    angle = angle - 360;

      left_pressed = 0;
	}

      /* Thrust ship: */
      if (up_pressed && player_alive)
	{
	  /* Move forward: */
	  xm = xm + ((fast_cos(angle >> 3) * 3) >> 10);
	  ym = ym - ((fast_sin(angle >> 3) * 3) >> 10);
	  
	  /* Start thruster sound: */
  if (use_sound)
	    {
	      if (!Mix_Playing(CHAN_THRUST))
		{
	  Mix_PlayChannel(CHAN_THRUST, sounds[SND_THRUST], -1);
		}
	    }
     up_pressed = 0;
	}
      else
        {
	  /* Slow down (unrealistic, but.. feh!) */
          if ((counter % 20) == 0)
	  {
            xm = (xm * 7) / 8;
	    ym = (ym * 7) / 8;
	  }
	  
	  /* Stop thruster sound: */
  if (use_sound)
	    {
	      if (Mix_Playing(CHAN_THRUST))
		{
	  Mix_HaltChannel(CHAN_THRUST);
	}
	    }
	}
      
      /* Handle player death: */
      if (player_alive == 0)
	{
	  player_die_timer--;
	  
	  if (player_die_timer <= 0)
	    {
	      if (lives > 0)
	      {
	        /* Reset player: */
  	        player_die_timer = 0;
	        angle = 90;
	        x = (WIDTH / 2) << 4;
	        y = (HEIGHT / 2) << 4;
	        xm = 0;
	        ym = 0;
	      
	        /* Only bring player back when it's alright to! */
	        player_alive = 1;
	     
	        if (!shift_pressed)
		{	
	          for (i = 0; i < NUM_ASTEROIDS && player_alive; i++)
	    	    {
	 	      if (asteroids[i].alive)
		        {
		          if (asteroids[i].x >= (x >> 4) - (WIDTH / 5) &&
			      asteroids[i].x <= (x >> 4) + (WIDTH / 5) &&
			      asteroids[i].y >= (y >> 4) - (HEIGHT / 5) &&
	 		      asteroids[i].y <= (y >> 4) + (HEIGHT / 5))
			    {
			      /* If any asteroid is too close for comfort,
			         don't bring ship back yet! */
			      player_alive = 0;
			    }
		        }
	 	    }
		}
	     }
	      else
	      {
           if (player_alive == 0)
           {
           done = 1;
           game_pending = 0;
           }
	     }
	    }
	}
      
      /* Erase screen: */
      SDL_BlitSurface(bkgd, NULL, screen, NULL);

      /* Move ship: */
      x = x + xm;
      y = y + ym;
      
      /* Wrap ship around edges of screen: */
      if (x >= (WIDTH << 4))
	x = x - (WIDTH << 4);
      else if (x < 0)
	x = x + (WIDTH << 4);
      
      if (y >= (HEIGHT << 4))
	y = y - (HEIGHT << 4);
      else if (y < 0)
	    y = y + (HEIGHT << 4);
      
      /* Move bullets: */
      for (i = 0; i < NUM_BULLETS; i++)
	{
	  if (bullets[i].timer >= 0)
	    {
	      /* Bullet wears out: */
	      bullets[i].timer--;
	      
	      /* Move bullet: */
	      bullets[i].x = bullets[i].x + bullets[i].xm;
	      bullets[i].y = bullets[i].y + bullets[i].ym;
	      
	      /* Wrap bullet around edges of screen: */
	      if (bullets[i].x >= WIDTH)
		bullets[i].x = bullets[i].x - WIDTH;
	      else if (bullets[i].x < 0)
		bullets[i].x = bullets[i].x + WIDTH;
	      
	      if (bullets[i].y >= HEIGHT)
		bullets[i].y = bullets[i].y - HEIGHT;
	      else if (bullets[i].y < 0)
		bullets[i].y = bullets[i].y + HEIGHT;
	      
	      
	      /* Check for collision with any asteroids! */
	      for (j = 0; j < NUM_ASTEROIDS; j++)
		{
		  if (bullets[i].timer > 0 && asteroids[j].alive)
		    {
		      if ((bullets[i].x + 5 >=
			   asteroids[j].x - asteroids[j].size * AST_RADIUS) &&
			  (bullets[i].x - 5<=
			   asteroids[j].x + asteroids[j].size * AST_RADIUS) &&
			  (bullets[i].y + 5 >=
			   asteroids[j].y - asteroids[j].size * AST_RADIUS) &&
			  (bullets[i].y - 5 <=
			   asteroids[j].y + asteroids[j].size * AST_RADIUS))
			{
			  /* Remove bullet! */
			  bullets[i].timer = 0;
			  
			  hurt_asteroid(j, bullets[i].xm, bullets[i].ym,
					asteroids[j].size * 3);
			}
		    }
		}
	    }
	}
      
      /* Move asteroids: */
      num_asteroids_alive = 0;
      
      for (i = 0; i < NUM_ASTEROIDS; i++)
	{
	  if (asteroids[i].alive)
	    {
	      num_asteroids_alive++;
	      
	      /* Move asteroid: */
	      if ((counter % 4) == 0)
		{
		  asteroids[i].x = asteroids[i].x + asteroids[i].xm;
		  asteroids[i].y = asteroids[i].y + asteroids[i].ym;
		}
	      
	      /* Wrap asteroid around edges of screen: */
	      if (asteroids[i].x >= WIDTH)
		asteroids[i].x = asteroids[i].x - WIDTH;
	      else if (asteroids[i].x < 0)
		asteroids[i].x = asteroids[i].x + WIDTH;
	      
	      if (asteroids[i].y >= HEIGHT)
		asteroids[i].y = asteroids[i].y - HEIGHT;
	      else if (asteroids[i].y < 0)
		asteroids[i].y = asteroids[i].y + HEIGHT;
	      
	      /* Rotate asteroid: */
	      asteroids[i].angle = (asteroids[i].angle +
				    asteroids[i].angle_m);
	      
	      /* Wrap rotation angle... */
	      if (asteroids[i].angle < 0)
		asteroids[i].angle = asteroids[i].angle + 360;
	      else if (asteroids[i].angle >= 360)
		asteroids[i].angle = asteroids[i].angle - 360;
	      
	      /* See if we collided with the player: */
	      if (asteroids[i].x >= (x >> 4) - SHIP_RADIUS &&
		  asteroids[i].x <= (x >> 4) + SHIP_RADIUS &&
		  asteroids[i].y >= (y >> 4) - SHIP_RADIUS &&
		  asteroids[i].y <= (y >> 4) + SHIP_RADIUS &&
		  player_alive)
		{
		  hurt_asteroid(i, xm >> 4, ym >> 4, NUM_BITS);
		  
		  player_alive = 0;
		  player_die_timer = 30;
		  
		  playsound(SND_EXPLODE);

		  /* Stop thruster sound: */
	  if (use_sound)
		    {
		      if (Mix_Playing(CHAN_THRUST))
			{
		  Mix_HaltChannel(CHAN_THRUST);
		}
		    }

		  lives--;

		  if (lives == 0)
		  {
	    if (use_sound)
		      {
			playsound(SND_GAMEOVER);
			playsound(SND_GAMEOVER);
			playsound(SND_GAMEOVER);
			/* Mix_PlayChannel(CHAN_THRUST,
			   sounds[SND_GAMEOVER], 0); */
		      }
            player_die_timer = 100;
		  }
		}
	    }
	}
      
      /* Move bits: */
      for (i = 0; i < NUM_BITS; i++)
	{
	  if (bits[i].timer > 0)
	    {
	      /* Countdown bit's lifespan: */
	      bits[i].timer--;
	      
	      
	      /* Move the bit: */
	      bits[i].x = bits[i].x + bits[i].xm;
	      bits[i].y = bits[i].y + bits[i].ym;

	      /* Wrap bit around edges of screen: */
	      if (bits[i].x >= WIDTH)
		bits[i].x = bits[i].x - WIDTH;
	      else if (bits[i].x < 0)
		bits[i].x = bits[i].x + WIDTH;
	      
	      if (bits[i].y >= HEIGHT)
		bits[i].y = bits[i].y - HEIGHT;
	      else if (bits[i].y < 0)
		bits[i].y = bits[i].y + HEIGHT;
	    }
	}

      /* Draw ship: */
      if (player_alive)
	{
	  draw_segment(SHIP_RADIUS, 0, mkcolor(128, 128, 255),
		       SHIP_RADIUS / 2, 135, mkcolor(0, 0, 192),
		       x >> 4, y >> 4,
		       angle);
	  
	  draw_segment(SHIP_RADIUS / 2, 135, mkcolor(0, 0, 192),
		       0, 0, mkcolor(64, 64, 230),
		       x >> 4, y >> 4,
		       angle);
	  
	  draw_segment(0, 0, mkcolor(64, 64, 230),
		       SHIP_RADIUS / 2, 225, mkcolor(0, 0, 192),
		       x >> 4, y >> 4,
		       angle);
	  
	  draw_segment(SHIP_RADIUS / 2, 225, mkcolor(0, 0, 192),
		       SHIP_RADIUS, 0, mkcolor(128, 128, 255),
		       x >> 4, y >> 4,
		       angle);
	  
	  /* Draw flame: */
	  if (up_pressed)
	    {
      draw_segment(0, 0, mkcolor(255, 255, 255),
			   (rand() % 20), 180, mkcolor(255, 0, 0),
			   x >> 4, y >> 4,
			   angle);
	    }
	}
      
      /* Draw bullets: */
      for (i = 0; i < NUM_BULLETS; i++)
	{
	  if (bullets[i].timer >= 0)
	    {
	      draw_line(bullets[i].x - (rand() % 3) - bullets[i].xm * 2,
			bullets[i].y - (rand() % 3) - bullets[i].ym * 2,
			mkcolor((rand() % 3) * 128,
				(rand() % 3) * 128,
				(rand() % 3) * 128),
			bullets[i].x + (rand() % 3) - bullets[i].xm * 2,
			bullets[i].y + (rand() % 3) - bullets[i].ym * 2,
			mkcolor((rand() % 3) * 128,
				(rand() % 3) * 128,
				(rand() % 3) * 128));
	      
	      draw_line(bullets[i].x + (rand() % 3) - bullets[i].xm * 2,
			bullets[i].y - (rand() % 3) - bullets[i].ym * 2,
			mkcolor((rand() % 3) * 128,
				(rand() % 3) * 128,
				(rand() % 3) * 128),
			bullets[i].x - (rand() % 3) - bullets[i].xm * 2,
			bullets[i].y + (rand() % 3) - bullets[i].ym * 2,
			mkcolor((rand() % 3) * 128,
				(rand() % 3) * 128,
				(rand() % 3) * 128));
	      
	      draw_thick_line(bullets[i].x - (rand() % 5),
			      bullets[i].y - (rand() % 5),
			      mkcolor((rand() % 3) * 128 + 64,
				      (rand() % 3) * 128 + 64,
				      (rand() % 3) * 128 + 64),
			      bullets[i].x + (rand() % 5),
			      bullets[i].y + (rand() % 5),
			      mkcolor((rand() % 3) * 128 + 64,
				      (rand() % 3) * 128 + 64,
				      (rand() % 3) * 128 + 64));
	      
	      draw_thick_line(bullets[i].x + (rand() % 5),
			      bullets[i].y - (rand() % 5),
			      mkcolor((rand() % 3) * 128 + 64,
				      (rand() % 3) * 128 + 64,
				      (rand() % 3) * 128 + 64),
			      bullets[i].x - (rand() % 5),
			      bullets[i].y + (rand() % 5),
			      mkcolor((rand() % 3) * 128 + 64,
				      (rand() % 3) * 128 + 64,
				      (rand() % 3) * 128 + 64));
	    }
	}
      
      /* Draw asteroids: */
      for (i = 0; i < NUM_ASTEROIDS; i++)
	{
	  if (asteroids[i].alive)
	    {
	      draw_asteroid(asteroids[i].size,
			    asteroids[i].x, asteroids[i].y,
			    asteroids[i].angle,
			    asteroids[i].shape);
	    }
	}

      /* Draw bits: */
      for (i = 0; i < NUM_BITS; i++)
	{
	  if (bits[i].timer > 0)
	    {
	      draw_line(bits[i].x, bits[i].y, mkcolor(255, 255, 255),
	                bits[i].x + bits[i].xm,
		       	bits[i].y + bits[i].ym, mkcolor(255, 255, 255));
	    }
	}

      /* Draw score: */
      sprintf(str, "%.6d", score);
      draw_text(str, 35, 26, 14, mkcolor(255, 255, 255));
      draw_text(str, 36, 27, 14, mkcolor(255, 255, 255));

      /* Level: */
      sprintf(str, "%d", level);
      draw_text(str, (WIDTH - 14) / 2, 26, 14, mkcolor(255, 255, 255));
      draw_text(str, (WIDTH - 14) / 2 + 1, 27, 14, mkcolor(255, 255, 255));

      /* High: */
      sprintf(str, "HIGH %.6d", high);
      draw_text(str, 35, 57, 7, mkcolor(128, 255, 255));
      draw_text(str, 36, 58, 7, mkcolor(128, 255, 255));

      /* Last: */
      sprintf(str, "LAST %.6d", last);
      draw_text(str, 35, 80, 7, mkcolor(128, 255, 255));
      draw_text(str, 36, 81, 7, mkcolor(128, 255, 255));

      /* Draw lives: */
      for (i = 0; i < lives; i++)
	{
	  draw_segment(16, 0, mkcolor(255, 255, 255),
		       4, 135, mkcolor(255, 255, 255),
		       WIDTH - 10 - i * 10, 20,
		       90);
	  
	  draw_segment(8, 135, mkcolor(255, 255, 255),
		       0, 0, mkcolor(255, 255, 255),
		       WIDTH - 10 - i * 10, 20,
		       90);
	  
	  draw_segment(0, 0, mkcolor(255, 255, 255),
		       8, 225, mkcolor(255, 255, 255),
		       WIDTH - 10 - i * 10, 20,
		       90);
	  
	  draw_segment(8, 225, mkcolor(255, 255, 255),
		       16, 0, mkcolor(255, 255, 255),
		       WIDTH - 10 - i * 10, 20,
		       90);
	}
      
      if (player_die_timer > 0)
	{
	  if (player_die_timer > 30)
	    j = 30;
	  else
	    j = player_die_timer;
	  
	  draw_segment((16 * j) / 30, 0, mkcolor(255, 255, 255),
		       (4 * j) / 30, 135, mkcolor(255, 255, 255),
		       WIDTH - 10 - i * 10, 20,
		       90);
	  
	  draw_segment((8 * j) / 30, 135, mkcolor(255, 255, 255),
		       0, 0, mkcolor(255, 255, 255),
		       WIDTH - 10 - i * 10, 20,
		       90);
	  
	  draw_segment(0, 0, mkcolor(255, 255, 255),
		       (8 * j) / 30, 225, mkcolor(255, 255, 255),
		       WIDTH - 10 - i * 10, 20,
		       90);
	  
	  draw_segment((8 * j) / 30, 225, mkcolor(255, 255, 255),
		       (16 * j) / 30, 0, mkcolor(255, 255, 255),
		       WIDTH - 10 - i * 10, 20,
		       90);

	}

      /* Zooming level effect: */
      if (text_zoom > 0)
	{
	  if ((counter % 2) == 0)
	    text_zoom--;

  draw_text(zoom_str, (WIDTH - (strlen(zoom_str) * text_zoom)) / 2,
		    (HEIGHT - text_zoom) / 2,
		    text_zoom, mkcolor(text_zoom * (256 / ZOOM_START), 0, 0));
	}

      /* Game over? */
      if (player_alive == 0 && lives == 0)
      {
	if (player_die_timer > 14)
	{
	  draw_text("GAME OVER",
	            (WIDTH - 9 * player_die_timer) / 2,
	  	    (HEIGHT - player_die_timer) / 2,
		    player_die_timer,
		    mkcolor(rand() % 255,
			    rand() % 255,
			    rand() % 255));
	}
	else
	{
	  draw_text("GAME OVER",
	            (WIDTH - 9 * 14) / 2,
                    (HEIGHT - 14) / 2,
                    14,
		    mkcolor(255, 255, 255));
	}
      }
      
      /* Go to next level? */
      if (num_asteroids_alive == 0)
	{
	  level++;
	  
	  reset_level();
	}
      
      /* Flush and pause! */
      SDL_Flip(screen);
      
      now_time = SDL_GetTicks();
      
      if (now_time < last_time + (1000 / FPS))
	{
	  SDL_Delay(last_time + 1000 / FPS - now_time);
	}
    }
  while (!done);

  /* Record, if a high score: */
  if (score >= high)
  {
    high = score;
  }

  /* Display mouse cursor: */
  if (fullscreen)
    SDL_ShowCursor (SDL_DISABLE);

  return(quit);
}


void finish(void)
{
  SDL_Quit();
}

void setup()
{
  int i;
  SDL_Surface * tmp;
  
  /* Options: */
  score = 0;
  use_sound = TRUE;
  fullscreen = TRUE;
  
  /* Seed random number generator: */

  srand(SDL_GetTicks());
  
  /* Init SDL video: */
  if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
      fprintf(stderr,
              "\nError: I could not initialize video!\n"
              "The Simple DirectMedia error that occured was:\n"
              "%s\n\n", SDL_GetError());
      exit(1);
    }
/* Open window: */
    if (fullscreen)
    {
      screen = SDL_SetVideoMode(640, 480, 16, SDL_FULLSCREEN & SDL_HWSURFACE);
      
      if (screen == NULL)
        {
          fprintf(stderr,
                  "\nWarning: I could not set up fullscreen video for "
                  "%dx%d mode.\n"
                  "The Simple DirectMedia error that occured was:\n"
                  "%s\n\n", WIDTH, HEIGHT, SDL_GetError());
          fullscreen = 0;
        }
    }
  

  /* Load background image: */
  tmp = IMG_Load(DATA_PREFIX "images/redspot.png");

  if (tmp == NULL)
    {
      fprintf(stderr,
	      "\nError: I could not open the background image:\n"
	      DATA_PREFIX "images/redspot.jpg\n"
	      "The Simple DirectMedia error that occured was:\n"
	      "%s\n\n", SDL_GetError());
      exit(1);
    }
  
  bkgd = SDL_DisplayFormat(tmp);
  if (bkgd == NULL)
    {
      fprintf(stderr,
	      "\nError: I couldn't convert the background image"
	      "to the display format!\n"
	      "The Simple DirectMedia error that occured was:\n"
	      "%s\n\n", SDL_GetError());
      exit(1);
    }
  
  SDL_FreeSurface(tmp);

 /* Init sound: */
  if (use_sound)
    {
      if (Mix_OpenAudio(22050, AUDIO_U8, 1, 512) < 0)
	{
	  fprintf(stderr,
                  "\nWarning: I could not set up audio for 22050 Hz "
                  "16-bit stereo.\n"
                  "The Simple DirectMedia error that occured was:\n"
                  "%s\n\n", SDL_GetError());
          use_sound = FALSE;
	}
    }
  
  /* Load sound files: */
  if (use_sound)
    {
      for (i = 0; i < NUM_SOUNDS; i++)
	{
	  sounds[i] = Mix_LoadWAV(sound_names[i]);
          if (sounds[i] == NULL)
            {
              fprintf(stderr,
                      "\nError: I could not load the sound file:\n"
                      "%s\n"
                      "The Simple DirectMedia error that occured was:\n"
                      "%s\n\n", sound_names[i], SDL_GetError());
              exit(1);
            }
	}
      
      game_music = Mix_LoadMUS(mus_game_name);
      if (game_music == NULL)
	{
	  fprintf(stderr,
		  "\nError: I could not load the music file:\n"
		  "%s\n"
		  "The Simple DirectMedia error that occured was:\n"
		  "%s\n\n", mus_game_name, SDL_GetError());
	  exit(1);
	}
    }
}

/* Fast approximate-integer, table-based cosine! Whee! */
int fast_cos(int angle)
{
  angle = (angle % 45);
  
  if (angle < 12)
    return(trig[angle]);
  else if (angle < 23)
    return(-trig[10 - (angle - 12)]);
  else if (angle < 34)
    return(-trig[angle - 22]);
  else
    return(trig[45 - angle]);
}

/* Sine based on fast cosine... */
int fast_sin(int angle)
{
  return(- fast_cos((angle + 11) % 45));
}

/* Draw a line: */
void draw_line(int x1, int y1, color_type c1,
	       int x2, int y2, color_type c2)
{
  sdl_drawline(x1, y1, c1, x2, y2, c2);
 
  if (x1 < 0 || x2 < 0)
    {
      sdl_drawline(x1 + WIDTH, y1, c1, x2 + WIDTH, y2, c2);
    }
  else if (x1 >= WIDTH || x2 >= WIDTH)
    {
      sdl_drawline(x1 - WIDTH, y1, c1, x2 - WIDTH, y2, c2);
    }
  
  if (y1 < 0 || y2 < 0)
    {
      sdl_drawline(x1, y1 + HEIGHT, c1, x2, y2 + HEIGHT, c2);
    }
  else if (y1 >= HEIGHT || y2 >= HEIGHT)
    {
      sdl_drawline(x1, y1 - HEIGHT, c1, x2, y2 - HEIGHT, c2);
    }
}

/* Create a color_type struct out of RGB values: */
color_type mkcolor(int r, int g, int b)
{
  color_type c;
  
  if (r > 255)
    r = 255;
  if (g > 255)
    g = 255;
  if (b > 255)
    b = 255;

  c.r = (Uint8) r;
  c.g = (Uint8) g;
  c.b = (Uint8) b;
  
  return c;
}

/* Draw a line on an SDL surface: */
void sdl_drawline(int x1, int y1, color_type c1,
		  int x2, int y2, color_type c2)
{
  int dx, dy;
  float cr, cg, cb, rd, gd, bd;
  float m, b;

  if (clip(&x1, &y1, &x2, &y2))
    {
      dx = x2 - x1;
      dy = y2 - y1;
      
      if (dx != 0)
        {
          m = ((float) dy) / ((float) dx);
          b = y1 - m * x1;
          
          if (x2 >= x1)
            dx = 1;
          else
            dx = -1;
         
          cr = c1.r;
          cg = c1.g;
          cb = c1.b;
          
          rd = (float) (c2.r - c1.r) / (float) (x2 - x1) * dx;
          gd = (float) (c2.g - c1.g) / (float) (x2 - x1) * dx;
          bd = (float) (c2.b - c1.b) / (float) (x2 - x1) * dx;

          while (x1 != x2)
            {
              y1 = m * x1 + b;
              y2 = m * (x1 + dx) + b;
              
             drawvertline(x1, y1, mkcolor(cr, cg, cb),
                           y2, mkcolor(cr + rd, cg + gd, cb + bd));

              x1 = x1 + dx;
              

              cr = cr + rd;
              cg = cg + gd;
              cb = cb + bd;
           }
        }
      else
        drawvertline(x1, y1, c1, y2, c2);
    }
}

/* Clip lines to window: */
int clip(int * x1, int * y1, int * x2, int * y2)
{
  float fx1, fx2, fy1, fy2, tmp;
  float m;
  unsigned char code1, code2;
  int done, draw, swapped;
  unsigned char ctmp;
  fx1 = (float) *x1;
  fy1 = (float) *y1;
  fx2 = (float) *x2;
  fy2 = (float) *y2;
  
  done = FALSE;
  draw = FALSE;
  m = 0;
  swapped = FALSE;
  
  while (!done)
    {
      code1 = encode(fx1, fy1);
      code2 = encode(fx2, fy2);
      
      if (!(code1 | code2))
        {
          done = TRUE;
          draw = TRUE;
        }
      else if (code1 & code2)
        {
          done = TRUE;
        }
      else
        {
          if (!code1)
            {
              swapped = TRUE;
              tmp = fx1;
              fx1 = fx2;
              fx2 = tmp;
              
              tmp = fy1;
              fy1 = fy2;
              fy2 = tmp;
              
              ctmp = code1;
              code1 = code2;
              code2 = ctmp;
            }
          
          if (fx2 != fx1)
            m = (fy2 - fy1) / (fx2 - fx1);
          else
            m = 1;
          
          if (code1 & LEFT_EDGE)
            {
              fy1 += ((0 - (fx1)) * m);
              fx1 = 0;
            }
          else if (code1 & RIGHT_EDGE)
            {
              fy1 += (((WIDTH - 1) - (fx1)) * m);
              fx1 = (WIDTH - 1);
            }
          else if (code1 & TOP_EDGE)
            {
              if (fx2 != fx1)
                fx1 += ((0 - (fy1)) / m);
              fy1 = 0;
            }
          else if (code1 & BOTTOM_EDGE)
            {
              if (fx2 != fx1)
                fx1 += (((HEIGHT - 1) - (fy1)) / m);
              fy1 = (HEIGHT - 1);
            }
        }
    }

  if (swapped)
    {
      tmp = fx1;
      fx1 = fx2;
      fx2 = tmp;
      
      tmp = fy1;
      fy1 = fy2;
      fy2 = tmp;
    }
 
  *x1 = (int) fx1;
  *y1 = (int) fy1;
  *x2 = (int) fx2;
  *y2 = (int) fy2;

  return(draw);
}

/* Where does this line clip? */
unsigned char encode(float x, float y)
{
  unsigned char code;
  
  code = 0x00;
  
  if (x < 0.0)
    code = code | LEFT_EDGE;
  else if (x >= (float) WIDTH)
    code = code | RIGHT_EDGE;
  
  if (y < 0.0)
    code = code | TOP_EDGE;
  else if (y >= (float) HEIGHT)
    code = code | BOTTOM_EDGE;
  
  return code;
}

/* Draw a verticle line: */
void drawvertline(int x, int y1, color_type c1,
                  int y2, color_type c2)
{
  int tmp, dy;
  float cr, cg, cb, rd, gd, bd;

  if (y1 > y2)
    {
      tmp = y1;
      y1 = y2;
      y2 = tmp;
      
      tmp = c1.r;
      c1.r = c2.r;
      c2.r = tmp;
      
      tmp = c1.g;
      c1.g = c2.g;
      c2.g = tmp;
      
      tmp = c1.b;
      c1.b = c2.b;
      c2.b = tmp;
    }
  
  cr = c1.r;
  cg = c1.g;
  cb = c1.b;
  
  if (y1 != y2)
    {
      rd = (float) (c2.r - c1.r) / (float) (y2 - y1);
      gd = (float) (c2.g - c1.g) / (float) (y2 - y1);
      bd = (float) (c2.b - c1.b) / (float) (y2 - y1);
    }
  else
    {
      rd = 0;
      gd = 0;
      bd = 0;
    }

  for (dy = y1; dy <= y2; dy++)
    {
      putpixel(screen, x + 1, dy + 1, SDL_MapRGB(screen->format, 0, 0, 0));
      
      putpixel(screen, x, dy, SDL_MapRGB(screen->format,
                                         (Uint8) cr,
                                         (Uint8) cg,
                                         (Uint8) cb));

      cr = cr + rd;
      cg = cg + gd;
      cb = cb + bd;
    }
}

/* Draw a single pixel into the surface: */
void putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel)
{
  int bpp;
  Uint8 * p;

  /* Assuming the X/Y values are within the bounds of this surface... */
  if (x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT)
    {
      /* Determine bytes-per-pixel for the surface in question: */
      bpp = surface->format->BytesPerPixel;
      
      /* Set a pointer to the exact location in memory of the pixel
         in question: */
      p = (((Uint8 *) surface->pixels) +       /* Start at beginning of RAM */
	   (y * surface->pitch) +  /* Go down Y lines */
	   (x * bpp));             /* Go in X pixels */
      
      /* Set the (correctly-sized) piece of data in the surface's RAM
         to the pixel value sent in: */
      if (bpp == 1)
        *p = pixel;
      else if (bpp == 2)
        *(Uint16 *)p = pixel;
      else if (bpp == 3)
        {
          if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
            {
              p[0] = (pixel >> 16) & 0xff;
              p[1] = (pixel >> 8) & 0xff;
              p[2] = pixel & 0xff;
            }
          else
            {
              p[0] = pixel & 0xff;
              p[1] = (pixel >> 8) & 0xff;
              p[2] = (pixel >> 16) & 0xff;
            }
        }
      else if (bpp == 4)
        {
          *(Uint32 *)p = pixel;
        }
    }
}

/* Draw a line segment, rotated around a center point: */
void draw_segment(int r1, int a1,
		  color_type c1,
		  int r2, int a2,
		  color_type c2,
		  int cx, int cy, int a)
{
  draw_line(((fast_cos((a1 + a) >> 3) * r1) >> 10) + cx,
	    cy - ((fast_sin((a1 + a) >> 3) * r1) >> 10),
	    c1,
	    ((fast_cos((a2 + a) >> 3) * r2) >> 10) + cx,
	    cy - ((fast_sin((a2 + a) >> 3) * r2) >> 10),
	    c2);
}

/* Add a bullet: */
void add_bullet(int x, int y, int a, int xm, int ym)
{
  int i, found;
  found = -1;
  
  for (i = 0; i < NUM_BULLETS && found == -1; i++)
    {
      if (bullets[i].timer <= 0)
	found = i;
    }
  
  if (found != -1)
    {
     bullets[found].timer = 50;

      bullets[found].x = x;
      bullets[found].y = y;
      
      bullets[found].xm = ((fast_cos(a >> 3) * 5) >> 10) + (xm >> 4);
      bullets[found].ym = - ((fast_sin(a >> 3) * 5) >> 10) + (ym >> 4);
      
      playsound(SND_BULLET);
    }
}

/* Add an asteroid: */
void add_asteroid(int x, int y, int xm, int ym, int size)
{
  int i, found;
  
  /* Find a slot: */
  found = -1;
  
  for (i = 0; i < NUM_ASTEROIDS && found == -1; i++)
    {
      if (asteroids[i].alive == 0)
	found = i;
    }
  
  /* Hack: No asteroids should be stationary! */
   while (xm == 0)
    {
      xm = (rand() % 3) - 1;
    }

  if (found != -1)
    {
      asteroids[found].alive = 1;
      asteroids[found].x = x;
      asteroids[found].y = y;
      asteroids[found].xm = xm;
      asteroids[found].ym = ym;
      asteroids[found].angle = (rand() % 360);
      asteroids[found].angle_m = (rand() % 6) - 3;
      asteroids[found].size = size;
      
      for (i = 0; i < AST_SIDES; i++)
	{
	  asteroids[found].shape[i].radius = (rand() % 3);
	  asteroids[found].shape[i].angle = i * 60 + (rand() % 40);
	}
    }
}

/* Add a bit: */
void add_bit(int x, int y, int xm, int ym)
{
  int i, found;
  found = -1;
  
  for (i = 0; i < NUM_BITS && found == -1; i++)
    {
      if (bits[i].timer <= 0)
	found = i;
    }
  
  if (found != -1)
    {
      bits[found].timer = 16;
      
      bits[found].x = x;
      bits[found].y = y;
      bits[found].xm = xm;
      bits[found].ym = ym;
    }
}

/* Draw an asteroid: */
void draw_asteroid(int size, int x, int y, int angle, shape_type * shape)
{
  int i, b1, b2;
  int div;
  
 div = 240;

  for (i = 0; i < AST_SIDES - 1; i++)
    {
      b1 = (((shape[i].angle + angle) % 180) * 255) / div;
      b2 = (((shape[i + 1].angle + angle) % 180) * 255) / div;
      
      draw_segment((size * (AST_RADIUS - shape[i].radius)),
		   shape[i].angle, mkcolor(b1, b1, b1),
		   (size * (AST_RADIUS - shape[i + 1].radius)),
		   shape[i + 1].angle, mkcolor(b2, b2, b2),
		   x, y,
		   angle);
    }

  b1 = (((shape[AST_SIDES - 1].angle + angle) % 180) * 255) / div;
  b2 = (((shape[0].angle + angle) % 180) * 255) / div;

  draw_segment((size * (AST_RADIUS - shape[AST_SIDES - 1].radius)),
	       shape[AST_SIDES - 1].angle, mkcolor(b1, b1, b1),
	       (size * (AST_RADIUS - shape[0].radius)),
	       shape[0].angle, mkcolor(b2, b2, b2),
	       x, y,
	       angle);
}

/* Queue a sound! */
void playsound(int snd)
{
  int which, i;
  
  if (use_sound)
    {
      which = (rand() % 3) + CHAN_THRUST;
      for (i = CHAN_THRUST; i < 4; i++)
	{
	  if (!Mix_Playing(i))
	    which = i;
	}
      Mix_PlayChannel(which, sounds[snd], 0);
    }
}

/* Break an asteroid and add an explosion: */
void hurt_asteroid(int j, int xm, int ym, int exp_size)
{
  int k;
  add_score(100 / (asteroids[j].size + 1));

  if (asteroids[j].size > 1)
    {
      /* Break the rock into two smaller ones! */
      add_asteroid(asteroids[j].x,
		   asteroids[j].y,
		   ((asteroids[j].xm + xm) / 2),
		   (asteroids[j].ym + ym),
		   asteroids[j].size - 1);
      
      add_asteroid(asteroids[j].x,
		   asteroids[j].y,
		   (asteroids[j].xm + xm),
		   ((asteroids[j].ym + ym) / 2),
		   asteroids[j].size - 1);
    }
  
  /* Make the original go away: */
  asteroids[j].alive = 0;
  
  /* Add explosion: */
  playsound(SND_AST1 + (asteroids[j].size) - 1);
  
  for (k = 0; k < exp_size; k++)
    {
      add_bit((asteroids[j].x -
	       (asteroids[j].size * AST_RADIUS) +
	       (rand() % (AST_RADIUS * 2))),
	      (asteroids[j].y -
	       (asteroids[j].size * AST_RADIUS) +
	       (rand() % (AST_RADIUS * 2))),
	      ((rand() % (asteroids[j].size * 3)) -
	       (asteroids[j].size) +
	       ((xm + asteroids[j].xm) / 3)),
	      ((rand() % (asteroids[j].size * 3)) -
	       (asteroids[j].size) +
	       ((ym + asteroids[j].ym) / 3)));
    }
}

/* Increment score: */
void add_score(int amount)
{
  /* See if they deserve a new life: */
  if (score / ONEUP_SCORE < (score + amount) / ONEUP_SCORE)
  {
    lives++;
    strcpy(zoom_str, "EXTRA LIFE");
    text_zoom = ZOOM_START;
    playsound(SND_EXTRALIFE);
  }

  /* Add to score: */
  score = score + amount;
}

/* Draw a character: */
void draw_char(char c, int x, int y, int r, color_type cl)
{
  int i, v;
  
  /* Which vector is this character? */
  v = -1;
  if (c >= '0' && c <= '9')
    v = (c - '0');
  else if (c >= 'A' && c <= 'Z')
    v = (c - 'A') + 10;
  
  if (v != -1)
    {
      for (i = 0; i < 5; i++)
	{
	  if (char_vectors[v][i][0] != -1)
	    {
	      draw_line(x + (char_vectors[v][i][0] * r),
			y + (char_vectors[v][i][1] * r),
			cl,
			x + (char_vectors[v][i][2] * r),
			y + (char_vectors[v][i][3] * r),
			cl);
	    }
	}
    }
}

void draw_text(char * str, int x, int y, int s, color_type c)
{
  int i;

  for (i = 0; i < strlen(str); i++)
    draw_char(str[i], i * (s + 3) + x, y, s, c);
}

void draw_thick_line(int x1, int y1, color_type c1,
		     int x2, int y2, color_type c2)
{
  draw_line(x1, y1, c1, x2, y2, c2);
  draw_line(x1 + 1, y1 + 1, c1, x2 + 1, y2 + 1, c2);
}

void reset_level(void)
{
  int i;
  
  for (i = 0; i < NUM_BULLETS; i++)
    bullets[i].timer = 0;
  
  for (i = 0; i < NUM_ASTEROIDS; i++)
    asteroids[i].alive = 0;
  
  for (i = 0; i < NUM_BITS; i++)
    bits[i].timer = 0;
  
  for (i = 0; i < (level + 1) && i < 10; i++)
    {
     add_asteroid(/* x */ (rand() % 40) + ((WIDTH - 40) * (rand() % 2)),
		   /* y */ (rand() % HEIGHT),
		   /* xm */ (rand() % 9) - 4,
		   /* ym */ ((rand() % 9) - 4) * 4,
		   /* size */ (rand() % 3) + 2);
    }

  sprintf(zoom_str, "LEVEL %d", level);

  text_zoom = ZOOM_START;
}


/* Set video mode: */
/* Contributed to "Defendguin" by Mattias Engdegard <f91-men@nada.kth.se> */
SDL_Surface * set_vid_mode(unsigned flags)
{
  /* Prefer 16bpp, but also prefer native modes to emulated 16bpp. */
  int depth;
  
  depth = SDL_VideoModeOK(WIDTH, HEIGHT, 16, flags);
  return depth ? SDL_SetVideoMode(WIDTH, HEIGHT, depth, flags) : NULL;
}

/* Draw text, centered horizontally: */
void draw_centered_text(char * str, int y, int s, color_type c)
{
  draw_text(str, (WIDTH - strlen(str) * (s + 3)) / 2, y, s, c);
}


