#define __RAWKS_SHIP__
#define DBG 0
#define USEOLD 0

#include "defines.h"
#include <stdio.h>
#include "struct.h"
#include "gl.h"
#include "gldevice.h"
#include "protos.h"
#include "globals.h"

#define MAX_THRUST		0.0002
#define THRUST_ACCEL		0.00004
#define BULLET_SPEED		0.006
#define BULLET_TIME_TO_LIVE	170
#define MAXBULLETS		4
#define DECELERATION_CONSTANT	0.998
#define ROT_SPEED		768.0

/* There is a lot of duplicate code in here, but frankly I don't have time
   to do it right... and the game is almost done etc etc
   code is duplicated from draw_line_rock() - ideally these functions
   should be merged and data structures modified to support such */

void draw_ship(int p)
{
  float s,c;
  int i,j;
  float x,y,px,py;

  s= Sintable[(int)(P[p].rotation[Frm]>>8)];
  c= Costable[(int)(P[p].rotation[Frm]>>8)];

  x = P[p].V[Frm].x;
  y = P[p].V[Frm].y;

  for(i=p+1;i<=Num_players;i++) { /* Check exemptions via bbox for ships */
			  /* Only checking against higher numbered ships */
    px = P[i].V[Frm].x;
    py = P[i].V[Frm].y;

    if(P[p].minx + x > P[i].maxx + px ||
       P[p].maxx + x < P[i].minx + px ||
       P[p].miny + y > P[i].maxy + py ||
       P[p].maxy + y < P[i].miny + py
      )
    P[p].exempt = P[i].exempt = 1; /* If bound boxes overlap, then
				      flag the intersection testing loop */
    else P[p].exempt = P[i].exempt = P[p].allclear = P[i].allclear = 0 ;
  }
  /* The idea here is to check each ship against each bullet to
     cull out most ships before having to check each line against each
     bullet */
  for(i=Firstbullet_i;i!=Nextbullet_i;i=INC_BULLET(i)) {
    int f2xltf0x,f2yltf0y;

    /* 2+Frm = Frm + 2*inertia */
    f2xltf0x = (B[i].inertiax > 0.0)?0:2;
    f2yltf0y = (B[i].inertiay > 0.0)?0:2;
    
    if(P[p].minx + x > B[i].V[Frm+2-f2xltf0x ].x ||
       P[p].maxx + x < B[i].V[Frm+  f2xltf0x ].x ||
       P[p].miny + y > B[i].V[Frm+2-f2yltf0y ].y ||
       P[p].maxy + y < B[i].V[Frm+  f2yltf0y ].y
      )
      B[i].exempt=1;
    else B[i].exempt=0;
  }

#if DBG
  sbox(P[p].minx+P[p].V[Frm].x,
       P[p].miny+P[p].V[Frm].y,
       P[p].maxx+P[p].V[Frm].x,
       P[p].maxy+P[p].V[Frm].y);
#endif

  Flag = 0; /* Clear flag for first vertex */
  bgnline();
  for(j=0;j<P[p].ship->num_verts;j++) {
    static float t[2];

    t[0] = P[p].V[Frm].x + P[p].ship->vertex[P[p].rot[Frm]][j]->x;
    t[1] = P[p].V[Frm].y + P[p].ship->vertex[P[p].rot[Frm]][j]->y;

    P[p].modg->verts[j].x = t[0];
    P[p].modg->verts[j].y = t[1];

    v2f_line_ship(p,t);
/*     v2f(t); */
  }
  endline();
}

#if 0
/* This was made to help me find a bug */
static void check_rot_bounds(int i)
{
  P[i].rot[Frm]=(int)((P[i].rotation[Frm]/65536.0)*P[i].ship->num_frames);
  if(P[i].rot[Frm] >= P[i].ship->num_frames || P[i].rot[Frm] < 0)
    fprintf(stderr,"Out of bounds rot\n");
  while(P[i].rot[Frm] >= P[i].ship->num_frames)
        P[i].rot[Frm] -= P[i].ship->num_frames;
  while(P[i].rot[Frm]< 0.0)
        P[i].rot[Frm] += P[i].ship->num_frames;
}
#endif

void rotate_left(int i)
{
  P[i].rotation[Frm]=P[i].rotation[Old_frm]+(unsigned short)(ROT_SPEED*D_time);
  P[i].rot[Frm]=(int)((P[i].rotation[Frm]/65536.0)*P[i].ship->num_frames);
/*   check_rot_bounds(i); */
}

void rotate_right(int i)
{
  P[i].rotation[Frm]=P[i].rotation[Old_frm]-(unsigned short)(ROT_SPEED*D_time);
  P[i].rot[Frm]=(int)((P[i].rotation[Frm]/65536.0)*P[i].ship->num_frames);
/*   check_rot_bounds(i); */
}

static void seat_player(int player)
{
  int i;
  static float pstartpos[4][4][2] = { /* Assigned seating chart */
    { { 0.0 , 0.0 } ,{ 0.0 , 0.0 } ,{ 0.0 , 0.0 } ,{ 0.0 , 0.0 } ,},
    { {-0.5 , 0.0 } ,{ 0.5 , 0.0 } ,{ 0.0 , 0.0 } ,{ 0.0 , 0.0 } ,},
    { {-0.5 , 0.5 } ,{ 0.5 , 0.5 } ,{-0.5 ,-0.5 } ,{ 0.0 , 0.0 } ,},
    { {-0.5 , 0.5 } ,{ 0.5 , 0.5 } ,{-0.5 ,-0.5 } ,{ 0.5 ,-0.5 } ,},
  };

  if(player < 1 || player > Num_players || Num_players < 1 || Num_players > 4)
    return;
  for(i=0;i<4;i++) {
    P[player].V[i].x=
     Level->startx+(pstartpos[Num_players-1][player-1][0])*Level->startboxsize;
    P[player].V[i].y=
     Level->startx+(pstartpos[Num_players-1][player-1][1])*Level->startboxsize;
  }
}

void update_ships(void)
{
  int should_cont = 0;
  int i;

#if DBG
  DEBUG("update_ships()");
#endif

  for(i=1;i<=Num_players;i++) if(P[i].dead) {
    thrust(i,0); /* Turn ships thrusters off */
    C_BLACK;
    P[i].thrusting = 1; /* Historic reasons for this line */
    draw_ship(i);
    P[i].thrusting = 0;

    if(P[i].numlives <= 0) continue;
    should_cont = 1;
    if(P[i].dead < 0.0) {
      if(P[i].dead == -1 /* && P[i].allclear */) {
      /* If let up on thrust but still dead, and no rock on top of him */
	cpack(Frm?0:P[i].color); /* Blink ship until alive again */
	draw_ship(i);
      }
      if (getvaluator(P[i].thrustkey) || getvaluator(P[i].firekey)) {
      /* If press thrust and there is no rock on top of the guy */
	if(P[i].dead != -1.0) continue; /* If not let up on keys */
	P[i].dead = 0.0;
	P[i].message[0] = 0; /* Turn off stats message */
	Show_high = 0;
        update_overlay(OLAY_CLEAR); /* Clear the high scores */
	--i; /* Let them go (by looping this i again) */
      }
      else if(P[i].dead != -1) {
	P[i].dead = -1.0; /* Info that thrust key is up */
	sfx(SFX_ALIVE);   /* One time initialization stuff */
	seat_player(i);
	P[i].inertiax    = P[i].inertiay = 0.0;
	P[i].rotation[0] = P[i].rotation[1] = 0.0;
	P[i].rot[0]      = P[i].rot[1]      = 0;
      }
    } else P[i].dead -= D_time;
    P[i].allclear = 1;
  } else if(P[i].numlives) {
    int temp;

    P[i].finishtime += D_time;
    should_cont = 1;
    temp = P[i].thrusting;
    P[i].thrusting = 1;
    C_BLACK;
    draw_ship(i);
    P[i].thrusting = temp;
	 if(getvaluator(P[i].leftkey))   rotate_left(i);
    else if(getvaluator(P[i].rightkey))  rotate_right(i);
    else {
      P[i].rotation[Frm] = P[i].rotation[Old_frm];
      P[i].rot[Frm] = P[i].rot[Old_frm];
    }
    update_trajectory((trajectory *)&P[i]);


    if(P[i].nofire) P[i].nofire=getvaluator(P[i].firekey);
    else if(getvaluator(P[i].firekey)) P[i].nofire=!P[i].rapid,fire(i);

    decelerate(i);
    thrust(i,getvaluator(P[i].thrustkey));

    cpack(P[i].color);
    Collision_det = 1;
    draw_ship(i);
    Collision_det = 0;
  }
  if(!should_cont) {
    /* Do something fancy here */
    game_off(); /* If everyone is dead and no lives left */
  }
#if DBG
  DEBUG("update_ships complete");
#endif
}

update_trajectory(trajectory *t)
{
  float *x,*y;

  x = &(t->V[Frm].x);
  y = &(t->V[Frm].y);

  (*x) = t->V[Old_frm].x + (t->inertiax * D_time  );
  (*y) = t->V[Old_frm].y + (t->inertiay * D_time  );

  /* For uses like redrawing the screen */
  if((*x)>1.0) (*x)-=2.0; else if((*x)<-1.0) (*x)+=2.0;
  if((*y)>1.0) (*y)-=2.0; else if((*y)<-1.0) (*y)+=2.0;

  /* For checking collision detection */
  t->V[2+Frm].x = (*x) + 2*(t->inertiax * D_time  );
  t->V[2+Frm].y = (*y) + 2*(t->inertiay * D_time  );
}

void thrust(int i,int active)
{
#define NUM_THRUST_COLORS 7
  static unsigned int thrust_color[NUM_THRUST_COLORS] = {
    0x0000FF,0x00FFFF,0x0055FF,0x3399AA,0xAA0000,0x00AAFF,0x00FFAA
  };
  static int color_ctr = 0;
  static trajectory traj;
  int j;

  if(!active) {
    if(P[i].thrusting) {
      DEBUG("Trying to tell the guy");
      sfx_kill(SFX_THRUST);
      P[i].thrusting = 0;
    }
    return;
  }
  else if(!P[i].thrusting) {
    sfx(SFX_THRUST);
    P[i].thrusting = 1;
    P[i].message[0] = 0;
  }
  if(((P[i].inertiax * P[i].inertiax)
    + (P[i].inertiay * P[i].inertiay)) > MAX_THRUST ) {
    P[i].inertiax *= 0.99;
    P[i].inertiay *= 0.99;
    return;
  }

  traj.inertiax = -Sintable[(int)(P[i].rotation[Frm]>>8)]*THRUST_ACCEL;
  P[i].inertiax += traj.inertiax * D_time;

  traj.inertiay = Costable[(int)(P[i].rotation[Frm]>>8)]*THRUST_ACCEL;
  P[i].inertiay += traj.inertiay * D_time;

  traj.V[Old_frm].x = P[i].V[Frm].x ;
  traj.V[Old_frm].y = P[i].V[Frm].y ;
  update_trajectory(&traj);
  traj.inertiax *= -200;
  traj.inertiay *= -200;
  if(++color_ctr >= NUM_THRUST_COLORS) color_ctr = 0;
  traj.color = thrust_color[color_ctr];
  traj.ttl = 9;

  for(j=0;j<1;j++) { /* Thrust particles being emitted */
    new_particle(&traj);
  }
}

void decelerate(int i)
{
#if 0
  P[i].inertiax*=DECELERATION_CONSTANT;
  P[i].inertiay*=DECELERATION_CONSTANT;
#endif
}

void fire(int i)
{
  int temp;

  if(!i) syserr("No player #0!!!");
  if(P[i].numbullets >= MAXBULLETS) return;
  else P[i].fired++;
  if((temp = INC_BULLET(Nextbullet_i))==Firstbullet_i) return;
  else {

    sfx(SFX_FIRE);
    if(B[Nextbullet_i].ttl) return; /* System out of bullets */

    /* Increment number of outstanding bullets by player i */
    P[i].numbullets++; /* Player 0 is for explosions */

    /* Copy player trajectory */
    memcpy(&B[Nextbullet_i],&P[i],sizeof(trajectory));

    B[Nextbullet_i].ttl = BULLET_TIME_TO_LIVE;
    B[Nextbullet_i].player = i;

    /* add bullet trajectory */
    B[Nextbullet_i].inertiax+=-Sintable[(int)(P[i].rotation[Frm]>>8)]*BULLET_SPEED;
    B[Nextbullet_i].inertiay+= Costable[(int)(P[i].rotation[Frm]>>8)]*BULLET_SPEED;

    Nextbullet_i = temp;
  }
}

void draw_scene(void)
{
  Old_frm = Frm;

#ifndef __LINUX__
  Frm = !Frm;
#endif

  swapbuffers();
#if DBG
  DEBUG("draw_scene()");
#endif

#ifndef __LINUX__
  mmode(MVIEWING);
  loadmatrix(Identity);
#endif

  update_partics();
  if(!Game_over) update_ships();
#if DBG
  DEBUG("draw_scene: drawing rocks");
#endif
#ifdef __LINUX__
/*   vga_waitretrace(); */
#endif
  update_rocks();
#if DBG
  DEBUG("draw_scene: done drawing rocks");
#endif

#if DBG
  DEBUG("draw_scene: drawing bullets");
#endif
  update_bullets();
#if DBG
  DEBUG("draw_scene: done drawing bullets");
#endif

#if DBG
  DEBUG("draw_scene: drawing text");
#endif
  draw_text();
#if DBG
  DEBUG("draw_scene: done drawing text");
#endif
  /* do_collisions(); */
#if DBG
  DEBUG("draw_scene complete");
#endif
#ifdef __LINUX__
  update_overlay(OLAY_TEXT);
#endif
}

void update_bullets(void)
{
  static int j=0;
  int i;

  if(Firstbullet_i == Nextbullet_i) {
    Firstbullet_i = Nextbullet_i = 0;
    return;
  }
  for(i=Firstbullet_i;i!=Nextbullet_i;i=INC_BULLET(i)) {
    if(!B[i].ttl) {
      if(i==Firstbullet_i) Firstbullet_i = INC_BULLET(Firstbullet_i);
      continue;
    }

    bgnline();
    C_BLACK;
    v2f(&B[i].V[Frm].x);
    v2f(&B[i].V[2+Frm].x);
    endline();

    update_trajectory(&B[i]);

    if(B[i].ttl > D_time) {

      bgnline(); /* Assuming color is already black */
      v2f(&B[i].V[Frm].x);
      C_WHITE;
      v2f(&B[i].V[2+Frm].x);
      endline();

      B[i].ttl -= D_time;
    } else {
      if(B[i].ttl == -1.0) { /* If flagged */
	B[i].ttl = 0.0;     /* time to die */
	if(B[i].player>0) {
	  P[B[i].player].numbullets--;
	}
      } else B[i].ttl = -1.0;
    }

  }
}
