/* game.c --- game itself
 *
 * This file is gift-ware.  This file is given to you freely
 * as a gift.  You may use, modify, redistribute, and generally hack
 * it about in any way you like, and you do not have to give anyone
 * anything in return.
 *
 * I do not accept any responsibility for any effects, adverse or
 * otherwise, that this code may have on just about anything that
 * you can think of.  Use it at your own risk.
 *
 * Copyright (C) 1999  Michael Bukin
 */

#include <allegro.h>

#include "game.h"

static int game_over = 0;
static int game_paused = 0;

static volatile int elapsed_time = 0;
static int processed_time = 0;
static int skipped_frames = 0;
static int max_skipped_frames = 3;

static int frame_count = 0;
static int fps_count = 0;

/*
 * Game speed timer.
 */
static void
game_timer (void)
{
  elapsed_time++;
}
END_OF_FUNCTION (game_timer);

/*
 * FPS calculation timer.
 */
static void
fps_counter (void)
{
  fps_count = frame_count;
  frame_count = 0;
}

/*
 * Objects.
 */
struct object_t
{
  int x, xs;
  int y, ys;
  BITMAP *sprite;
  int state;
  int state_counter;
};

#define CONTROLLABLE	1

#define MAX_PLAYER_BULLET	1024
#define MAX_ALIEN		1024
#define MAX_ALIEN_BULLET	1024
#define MAX_FALLING_BODY	1024
#define MAX_FALLING_SMOKE	1024
#define MAX_SMOKE		1024

static struct object_t player;

static int player_bullet_count = 0;
static struct object_t player_bullet[MAX_PLAYER_BULLET];

static int alien_count = 0;
static struct object_t alien[MAX_ALIEN];

static int alien_bullet_count = 0;
static struct object_t alien_bullet[MAX_ALIEN_BULLET];

static int falling_body_count = 0;
static struct object_t falling_body[MAX_FALLING_BODY];

static int falling_smoke_count = 0;
static struct object_t falling_smoke[MAX_FALLING_SMOKE];

static int smoke_count = 0;
static struct object_t smoke[MAX_SMOKE];

static int draw_shadows_flag = 0;

static int player_minx, player_maxx, player_miny, player_maxy;
static int object_minx, object_maxx, object_miny, object_maxy;

#define SPEED_SHIFT 3
#define X(p) ((p) >> SPEED_SHIFT)
#define Y(p) ((p) >> SPEED_SHIFT)

static int player_max_speed = 32;
static int player_speed_delta = 2;

static int player_score = 0;

static int text_color = 255;

/*
 * Move everything one step.
 */
static void
move_everything (void)
{
  int i;

  /* Move player.  */
  if (player.state & CONTROLLABLE)
    {
      if ((key[KEY_LEFT]) || (joy[0].stick[0].axis[0].d1))
	{
	  /* Moving left.  */
	  if (player.xs > -player_max_speed)
	    player.xs -= player_speed_delta;
	}
      else if ((key[KEY_RIGHT]) || (joy[0].stick[0].axis[0].d2))
	{
	  /* Moving right.  */
	  if (player.xs < player_max_speed)
	    player.xs += player_speed_delta;
	}
      else
	{
	  /* Decelerate by x.  */
	  if (player.xs > 0)
	    player.xs -= player_speed_delta;
	  else if (player.xs < 0)
	    player.xs += player_speed_delta;
	}

      if ((key[KEY_UP]) || (joy[0].stick[0].axis[1].d1))
	{
	  /* Moving forward.  */
	  if (player.ys > -player_max_speed)
	    player.ys -= player_speed_delta;
	}
      else if ((key[KEY_DOWN]) || (joy[0].stick[0].axis[1].d2))
	{
	  /* Moving backward.  */
	  if (player.ys < player_max_speed)
	    player.ys += player_speed_delta;
	}
      else
	{
	  /* Decelerate by y.  */
	  if (player.ys > 0)
	    player.ys -= player_speed_delta;
	  else if (player.ys < 0)
	    player.ys += player_speed_delta;
	}
    }
  else
    {
      /* Animation sequence.  */
      if (--player.state_counter <= 0)
	{
	  /* End of sequence (switch states).  */
	}
    }

  player.x += player.xs;
  if (player.x < player_minx)
    {
      player.x = player_minx;
      player.xs = 0;
    }
  else if (player.x > player_maxx)
    {
      player.x = player_maxx;
      player.xs = 0;
    }
  player.y += player.ys;
  if (player.y < player_miny)
    {
      player.y = player_miny;
      player.ys = 0;
    }
  else if (player.y > player_maxy)
    {
      player.y = player_maxy;
      player.ys = 0;
    }

  /* Test for collisions with alien bullets.  */


  /* Move player's bullets.  */
  for (i = 0; i < player_bullet_count; i++)
    {
      player_bullet[i].x += player_bullet[i].xs;
      player_bullet[i].y += player_bullet[i].ys;
      if ((player_bullet[i].x < object_minx) || (player_bullet[i].x > object_maxx)
	  || (player_bullet[i].y < object_miny) || (player_bullet[i].y > object_maxy))
	{
	  /* Remove bullet.  */
	  player_bullet[i] = player_bullet[--player_bullet_count];
	}

      /* Test for collisions with aliens.  */
    }

  /* Move aliens.  */
  for (i = 0; i < alien_count; i++)
    {
      /* Alien decides where to move.  */

      /* Move alien.  */
      alien[i].x += alien[i].xs;
      alien[i].y += alien[i].ys;
      if ((alien[i].x < object_minx) || (alien[i].x > object_maxx)
	  || (alien[i].y < object_miny) || (alien[i].y > object_maxy))
	{
	  /* Remove alien.  */
	  alien[i] = alien[--alien_count];
	}

      /* Test for collisions with player bullets.  */
    }

  /* Move alien's bullets.  */
  for (i = 0; i < alien_bullet_count; i++)
    {
      /* Move alien bullet.  */
      alien_bullet[i].x += alien_bullet[i].xs;
      alien_bullet[i].y += alien_bullet[i].ys;
      if ((alien_bullet[i].x < object_minx) || (alien_bullet[i].x > object_maxx)
	  || (alien_bullet[i].y < object_miny) || (alien_bullet[i].y > object_maxy))
	{
	  /* Remove bullet.  */
	  alien_bullet[i] = alien_bullet[--alien_bullet_count];
	}

      /* Test for collision with player ship.  */
    }

  /* Move smoke.  */
  for (i = 0; i < smoke_count; i++)
    {
      smoke[i].x += smoke[i].xs;
      smoke[i].y += smoke[i].ys;
      if ((smoke[i].x < object_minx) || (smoke[i].x > object_maxx)
	  || (smoke[i].y < object_maxy) || (smoke[i].y > object_maxy))
	{
	  /* Remove smoke.  */
	  smoke[i] = smoke[--smoke_count];
	}

      /* Change smoke state.  */
    }

  /* Move falling bodies.  */
  for (i = 0; i < falling_body_count; i++)
    {
      falling_body[i].x += falling_body[i].xs;
      falling_body[i].y += falling_body[i].ys;
      if ((falling_body[i].x < object_minx) || (falling_body[i].x > object_maxx)
	  || (falling_body[i].y < object_miny) || (falling_body[i].y > object_maxy))
	{
	  /* Remove falling body.  */
	  falling_body[i] = falling_body[--falling_body_count];
	}

      /* Change falling body state.  */
    }

  /* Move falling smoke.  */
  for (i = 0; i < falling_smoke_count; i++)
    {
      falling_smoke[i].x += falling_smoke[i].xs;
      falling_smoke[i].y += falling_smoke[i].ys;
      if ((falling_smoke[i].x < object_minx) || (falling_smoke[i].x > object_maxx)
	  || (falling_smoke[i].y < object_miny) || (falling_smoke[i].y > object_maxy))
	{
	  /* Remove falling smoke.  */
	  falling_smoke[i] = falling_smoke[--falling_smoke_count];
	}

      /* Change state of falling smoke.  */
    }
}

/*
 * Draw everything.
 */
static void
draw_everything (BITMAP *buffer)
{
  int i;

  /* Draw ground.  */
  clear (buffer);

  /* Draw shadows.  */
  if (draw_shadows_flag)
    {
      /* Not decided yet.  */
    }

  /* Draw falling bodies.  */
  for (i = 0; i < falling_body_count; i++)
    draw_sprite (buffer, falling_body[i].sprite, X (falling_body[i].x), Y (falling_body[i].y));

  /* Draw falling smoke.  */
  for (i = 0; i < falling_smoke_count; i++)
    draw_sprite (buffer, falling_smoke[i].sprite, X (falling_smoke[i].x), Y (falling_smoke[i].y));

  /* Draw bullets.  */
  for (i = 0; i < alien_bullet_count; i++)
    draw_sprite (buffer, alien_bullet[i].sprite, X (alien_bullet[i].x), Y (alien_bullet[i].y));
  for (i = 0; i < player_bullet_count; i++)
    draw_sprite (buffer, player_bullet[i].sprite, X (player_bullet[i].x), Y (player_bullet[i].y));

  /* Draw aliens.  */
  for (i = 0; i < alien_count; i++)
    draw_sprite (buffer, alien[i].sprite, X (alien[i].x), Y (alien[i].y));

  /* Draw player.  */
  draw_sprite (buffer, player.sprite, X (player.x), Y (player.y));

  /* Draw smoke.  */
  for (i = 0; i < smoke_count; i++)
    draw_sprite (buffer, smoke[i].sprite, X (smoke[i].x), Y (smoke[i].y));

  /* Draw misc information (text).  */
  text_mode (-1);
  textprintf (buffer, font, 0, 0, text_color, "fps: %d;  score: %d", fps_count, player_score);
}

/*
 * Start game.
 */
void
start_game (void)
{
  BITMAP *buffer;

  /* Create double buffer.  */
  buffer = create_bitmap (SCREEN_W, SCREEN_H);
  if (buffer == 0)
    {
      allegro_exit ();
      allegro_message ("Can not allocate double buffer!");
      exit (EXIT_FAILURE);
    }

  /* Install game speed timer.  */
  LOCK_FUNCTION (game_timer);
  LOCK_VARIABLE (elapsed_time);
  install_int (game_timer, 20);
  processed_time = elapsed_time;
  skipped_frames = 0;

  /* Install fps counter.  */
  LOCK_FUNCTION (fps_counter);
  LOCK_VARIABLE (frame_count);
  LOCK_VARIABLE (fps_count);
  install_int (fps_counter, 1000);
  frame_count = 0;
  fps_count = 0;

  /* Start game.  */
  game_over = 0;
  game_paused = 0;

  /* Initialize player state.  */

  /* Main game loop.  */
  while (!game_over)
    {
      poll_keyboard ();
      poll_joystick ();

      if (!game_paused)
	{
	  while (elapsed_time != processed_time)
	    {
	      /* Move everything and increment time.  */
	      move_everything ();
	      processed_time++;

	      if (skipped_frames++ > max_skipped_frames)
		{
		  /* Too much frames skipped.  */
		  processed_time = elapsed_time;
		  break;
		}
	    }

	  /* This frame is not skipped.  */
	  skipped_frames = 0;
	}

      /* Draw everything.  */
      draw_everything (buffer);
      /* Blit buffer to screen.  */
      blit (buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
      /* Increment number of frames.  */
      frame_count++;
    }

  /* Cleanup.  */
  remove_int (fps_counter);
  remove_int (game_timer);

  destroy_bitmap (buffer);

  clear_keybuf ();
}

/*
 * game.c ends here
 */
