/****************************************************************************
 *
 * Ships.c -- Functions used for calculations and displaying of ships.
 *
 *-------------------------------------------------------------------------
 * Authors: Casper Gripenberg  (casper@alpha.hut.fi)
 *          Kjetil Jacobsen  (kjetilja@stud.cs.uit.no)
 *
 */

#include <stdlib.h>
#include <math.h>
#include <exec/types.h>             /* The Amiga data types file.         */
#include <libraries/dos.h>          /* Official return codes defined here */

#include <proto/intuition.h>
#include <proto/graphics.h>

#include "main_protos.h"
#include "points_protos.h"
#include "ships_protos.h"
#include "lists_protos.h"
#include "fuelpod_protos.h"

#include "common.h"
#include "defs.h"
#include "map.h"
#include "prefs.h"
#include "ships.h"

/*---------------------------------------------------------------------------*/

extern int sina[];
extern int cosa[];

extern AWorld World;

struct Coordinates triangle[] = {
  { 0,-11},
  { 7,  7},
  {-7,  7},
  {0, -11}
};

/*---------------------------------------------------------------------------*/

/*
 * init_ship -- Inizialize ship parameterers. (May be changed to accept
 *              parameters as function arguments later...)
 *
 */

void
init_ship( AShip *aShip )
{
  int i, j;
  ABase *shipbase;

  shipbase = get_base(aShip);
  /*
   * Should be made to handle out-of-bases situations more gracefully
   * in the future.
   */ 
  if (shipbase == NULL)
    cleanExit( RETURN_WARN, "** Out of bases for new ship." );

  shipbase->owner = aShip;
  aShip->base.x = shipbase->mapos.x * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
  aShip->base.y = shipbase->mapos.y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;

  aShip->shape     = triangle;  
  aShip->shapesize = sizeof(triangle)/sizeof(struct Coordinates);
  aShip->mass      = SHP_MASS;
  aShip->draw_it   = FALSE;
  aShip->fueling   = FALSE;
  aShip->fuel      = MAXFUEL/5;
  aShip->fuelcount = 0;

  for (i = 0; i < MY_BUFFERS; i++) {
    aShip->s_drawn[i] = FALSE;
    aShip->f_drawn[i] = FALSE;
  }

  aShip->pos.x     = aShip->base.x;
  aShip->pos.y     = aShip->base.y;
  aShip->xvel      = aShip->yvel   = 0;
  aShip->xcount    = aShip->ycount = 0;
  aShip->status    = 0;
  aShip->shields   = TRUE;
  aShip->power     = THR_POWER * PRECS;
  aShip->exhcount  = EXH_COUNT; 
  aShip->exhwidth  = EXH_WIDTH;
  aShip->exhdist   = EXH_DIST;
  aShip->exhlife   = 25;   /* 50 = one second on 50fps */
  aShip->rotspeed  = ROT_SPEED;
  aShip->angle     = 0;
  aShip->local     = TRUE;
  aShip->fireing   = FALSE;
  aShip->buldist   = BUL_DIST;
  aShip->bul_life  = BUL_LIFE;
  aShip->fw_nbul   = 3;
  aShip->bw_nbul   = 1;
  aShip->thrusting = FALSE;

  aShip->currc     = (struct Coordinates *) 
                      malloc( aShip->shapesize * sizeof(struct Coordinates) );
  if (aShip->currc == NULL)
    cleanExit( RETURN_WARN, "** Unable to allocate space for ship structure.\n" );

  aShip->prevc     = (struct Coordinates **)
                      malloc( MY_BUFFERS * sizeof(struct Coordinates) );
  if (aShip->prevc == NULL)
    cleanExit( RETURN_WARN, "** Unable to allocate space for ship structure.\n" );

  for (i = 0; i < MY_BUFFERS; i++) {
    if ( (aShip->prevc[i] = 
          (struct Coordinates *)
           malloc( aShip->shapesize * sizeof(struct Coordinates) )) == NULL )
      cleanExit( RETURN_WARN, "** Unable to allocate space for ship structure.\n" );
  }     

  /* Copy the shape into current coordinates */
  for(i = 0; i < aShip->shapesize; i++) {
    aShip->currc[i].x = aShip->shape[i].x;
    aShip->currc[i].y = aShip->shape[i].y;
  }
  for(i = 0; i < MY_BUFFERS; i++) {
    aShip->p_pos[i].x = aShip->p_pos[i].y = 0;
    for(j = 0; j < aShip->shapesize; j++) {
      aShip->prevc[i][j].x = aShip->shape[j].x;
      aShip->prevc[i][j].y = aShip->shape[j].y;
    }
  }

}

/*---------------------------------------------------------------------------*/

/*
 * landing -- Returns TRUE if the ship is in a landing configuration,
 *            ie. nose up, slow speed.
 *
 */
BOOL
landing( AShip *ship )
{
  if (ship->angle < 15 || ship->angle > 345)
    if (ship->yvel <= PRECS && ship->yvel >= 0)
      return TRUE;

  return FALSE;
}

/*---------------------------------------------------------------------------*/

/*
 * update_ship -- Updates the ship position, angle, fireing and exhaust. Checks
 *                for ship out of window range etc. etc..
 */
void
update_ship( AShip *aShip, UWORD buf, UWORD nframes )
{
  int i;
  int isin, icos;

  /*
   * Check if ship exploded 
   */
  if (aShip->status > 0)
    return;

  if (aShip->turning != NO || aShip->thrusting || aShip->fireing) {

    if (aShip->turning == RIGHT) {
      aShip->angle = (aShip->angle + aShip->rotspeed * nframes) % 360;
    } else if (aShip->turning == LEFT) {
      aShip->angle = (aShip->angle - aShip->rotspeed * nframes) % 360;
     }

    if (aShip->angle < 0)
      aShip->angle = 360 + aShip->angle;

    isin = sina[aShip->angle];
    icos = cosa[aShip->angle];

    if (aShip->turning != NO) {
      if (aShip->turning == YES) aShip->turning = NO;
      /* Rotate the points */
      for(i = 0; i < aShip->shapesize; i++) {
        aShip->currc[i].x = (aShip->shape[i].x * icos - aShip->shape[i].y * isin) >> SHFTPR;
        aShip->currc[i].y = (aShip->shape[i].x * isin + aShip->shape[i].y * icos) >> SHFTPR;
      }
    }
  }

  for (i = nframes; i > 0; i--) {
    if (aShip->thrusting && aShip->fuel > 0) {
      aShip->xvel += (isin * aShip->power) >> SHFTPR;
      aShip->yvel -= (icos * aShip->power) >> SHFTPR;
    }
    aShip->yvel += World.gravity;
    aShip->xcount += aShip->xvel;
    aShip->ycount += aShip->yvel;
  }

  /* Move ship */
  aShip->pos.x  += (aShip->xcount < 0) ? -((-aShip->xcount) >> SHFTPR) : (aShip->xcount >> SHFTPR);
  aShip->xcount  = aShip->xcount % PRECS;
  aShip->pos.y  += (aShip->ycount < 0) ? -((-aShip->ycount) >> SHFTPR) : (aShip->ycount >> SHFTPR);
  aShip->ycount  = aShip->ycount % PRECS;

  if (aShip->thrusting && aShip->fuel > 0) {
    add_exhaust(aShip, isin, icos, nframes);
    aShip->fuelcount += (nframes << 1);
  }

  if (aShip->fireing) {
    aShip->fireing = FALSE;
    if (!aShip->shields && aShip->fuel > 0) {
      /* Every bullet grabs one fuel */
      aShip->fuel -= aShip->fw_nbul + aShip->bw_nbul;
      add_bullets(aShip, isin, icos);
    }
  }

  if (aShip->shields)
    aShip->fuelcount += nframes;
  if (aShip->fuelcount >= 50) {
    aShip->fuelcount = 0;
    aShip->fuel--;
  }
  if (aShip->fuel <= 0) {
    aShip->shields = FALSE;
    aShip->fuel = 0;
  }

  if (aShip->fueling)
    fuel_ship(aShip);

  if (aShip->pos.x >= MAP_BLOCKSIZE*World.Width) {
    aShip->pos.x = MAP_BLOCKSIZE*World.Width-1;
    aShip->xvel  = -(aShip->xvel >> 1);
  } else if (aShip->pos.x < 0) {
    aShip->pos.x = 0;
    aShip->xvel  = -(aShip->xvel >> 1);
  }
  if (aShip->pos.y >= MAP_BLOCKSIZE*World.Height) {
    aShip->pos.y = MAP_BLOCKSIZE*World.Height-1;
    aShip->yvel  = -(aShip->yvel >> 1);
  } else if (aShip->pos.y < 0) {
    aShip->pos.y = 0;
    aShip->yvel  = -(aShip->yvel >> 1);
  }
}

/*---------------------------------------------------------------------------*/

/*
 * draw_ship.c -- Draws a ship on the display
 *
 */
void
draw_ship( AShip *aShip, struct RastPort *rp, UWORD buf )
{
  static int animno = 0;   /* Need this for shield animation */

  int bp_x, bp_y, i;
  int pos_x, pos_y;
  struct Coordinates *prevc;
  struct Coordinates *currc;

  prevc = aShip->prevc[buf];
  currc = aShip->currc;

  SetWriteMask(rp, 3l);
  SetAPen(rp, 0);

  /* Remove ship */
  if (aShip->local) {
    Move(rp, LOCL_X+prevc[0].x, LOCL_Y+prevc[0].y);
    for (i = 1; i < aShip->shapesize; i++) {
      Draw(rp, LOCL_X+prevc[i].x, LOCL_Y+prevc[i].y);
      /* Store new coordinates */
      prevc[i].x = currc[i].x;
      prevc[i].y = currc[i].y;
    }
    prevc[0].x = currc[0].x;
    prevc[0].y = currc[0].y;
   
    /*
     * Remove shield image...
     */
    if (aShip->sh_drawn[buf]) {
      BltBitMap(World.shld_bm, (SHL_SIZE*2+1)*(SHL_ANIM-1), 0, rp->BitMap, 
                LOCL_X-SHL_SIZE, LOCL_Y-SHL_SIZE,
                SHL_SIZE*2+1, SHL_SIZE*2+1, 0x22, 0x03, NULL);
    }
    aShip->sh_drawn[buf] = FALSE;
    
    /*
     * This removes the fuelline...
     */
    if (aShip->f_drawn[buf]) {
      aShip->f_drawn[buf] = FALSE;  
      Move(rp, LOCL_X, LOCL_Y);
      Draw(rp, aShip->fuell[buf].x, aShip->fuell[buf].y);
    }
  } else { 
    if (aShip->s_drawn[buf]) {
      pos_x = aShip->p_pos[buf].x;
      pos_y = aShip->p_pos[buf].y;
      
      if (aShip->sh_drawn[buf]) {
        BltBitMap(World.shld_bm, (SHL_SIZE*2+1)*(SHL_ANIM-1), 0, rp->BitMap, 
                  pos_x-SHL_SIZE, pos_y-SHL_SIZE,
                  SHL_SIZE*2+1, SHL_SIZE*2+1, 0x22, 0x03, NULL);
      }
      aShip->sh_drawn[buf] = FALSE;

      Move(rp, pos_x+prevc[0].x, pos_y+prevc[0].y);
      for (i = 1; i < aShip->shapesize; i++) {
        Draw(rp, pos_x+prevc[i].x, pos_y+prevc[i].y);
      }

      aShip->s_drawn[buf] = FALSE;
    }      
  }

  /*
   * Check if our ship is dead
   */
  if (aShip->status > 0) {
    aShip->status -= (World.framerate * 2);
    if (aShip->status <= 0) {
      /*
       * Been dead long enough, restore default values...
       */
      if (aShip->local)
        World.hudon = TRUE;
      aShip->fuel    = MAXFUEL/5;
      aShip->fueling = FALSE;
      aShip->fpod    = NULL;
      aShip->shields = TRUE;
      aShip->angle   = 0;
      aShip->turning = YES;
      aShip->pos.x = aShip->base.x;
      aShip->pos.y = aShip->base.y;
      aShip->xvel = aShip->xcount = 0;
      aShip->yvel = aShip->ycount = 0;
    }
    return;
  }

  SetAPen(rp, 3);

  /* 
   *  Draw ship..
   */
  if (aShip->local) {

   /* 
    * Animate the shield...
    */
    animno += World.framerate; 
    if (animno >= SHL_ANIM-1)
      animno -= SHL_ANIM-1;

    if (aShip->shields) {
      BltBitMap(World.shld_bm, (SHL_SIZE*2+1)*animno, 0, rp->BitMap, 
                LOCL_X-SHL_SIZE, LOCL_Y-SHL_SIZE,
                SHL_SIZE*2+1, SHL_SIZE*2+1, 0xEE, 0x03, NULL);
      aShip->sh_drawn[buf] = TRUE;
    }

    if (aShip->fueling) {
      bp_x = World.local_ship->pos.x-(SCR_WIDTH+MAP_BLOCKSIZE*2)/2;
      bp_y = World.local_ship->pos.y-(SCR_HEIGHT+MAP_BLOCKSIZE*2)/2;
      Move(rp, LOCL_X, LOCL_Y);
      aShip->fuell[buf].x = aShip->fpod->pos.x-bp_x;
      aShip->fuell[buf].y = aShip->fpod->pos.y-bp_y;
      Draw(rp, aShip->fuell[buf].x, aShip->fuell[buf].y);
      aShip->f_drawn[buf] = TRUE;
    }

    Move(rp, LOCL_X+currc[0].x, LOCL_Y+currc[0].y);
    for (i = 1; i < aShip->shapesize; i++)
      Draw(rp, LOCL_X+currc[i].x, LOCL_Y+currc[i].y);

  } else {
    /*
     * Remote ship fuelline drawing needs to be implemented. This
     * may require line clipping depending on how it's done.
     */
    if (aShip->draw_it) {
      bp_x = World.local_ship->pos.x-(SCR_WIDTH+MAP_BLOCKSIZE*2)/2;
      bp_y = World.local_ship->pos.y-(SCR_HEIGHT+MAP_BLOCKSIZE*2)/2;
      pos_x = aShip->pos.x-bp_x;
      pos_y = aShip->pos.y-bp_y;

      aShip->p_pos[buf].x = pos_x;
      aShip->p_pos[buf].y = pos_y;

      if (aShip->shields) {
        BltBitMap(World.shld_bm, (SHL_SIZE*2+1)*animno, 0, rp->BitMap, 
                  pos_x-SHL_SIZE, pos_y-SHL_SIZE,
                  SHL_SIZE*2+1, SHL_SIZE*2+1, 0xEE, 0x03, NULL);
        aShip->sh_drawn[buf] = TRUE;
      }

      Move(rp, pos_x+currc[0].x, pos_y+currc[0].y);
      for (i = 1; i < aShip->shapesize; i++) {
        Draw(rp, pos_x+currc[i].x, pos_y+currc[i].y);
        prevc[i].x = currc[i].x;
        prevc[i].y = currc[i].y;
      }
      prevc[0].x = currc[0].x;
      prevc[0].y = currc[0].y;
     

      aShip->s_drawn[buf] = TRUE;
    }
  }
}
