#include "Graphics.h"
#include "Sound.h"
#include "swis.h"
#include <kernel.h>

#define PLAYER_Y_START 100
#define PLAYER_X_SPEED 10
#define MAX_NPCS 5
#define MAX_STARS 10
#define MAX_PROJECTILES 15

//#defene PROFILE(X) lastprofile = clock(); X; printf("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t%s %i\n", #X, (clock() - lastprofile))
#define PROFILE(X) X
extern sound_composition_save(char *filename);
extern sound_composition_load(char *filename);

extern _kernel_swi_regs inreg;
extern _kernel_swi_regs outreg;

enum sprite_e{player_ship, durno_ship, ship_trgt, durno_ship2, ship2_trgt, player_shipl,player_shipr,explode_start,explode_shp2,explode_shp3,explode_end,photon1,photon2,plasma1,plasma2};
char *sprites[] = {"player_ship","durno_ship","ship_trgt","durno_ship2","ship2_trgt","player_shipl","player_shipr","explode_shp1","explode_shp2","explode_shp3","explode_shp4","photon1","photon2","plasma1","plasma2"};

char hudbuffer[63];

unsigned char lastnpcchannel = PCMCHANNEL_NPC1;

struct EntityLocation_s {
  short signed int X,Y;
};

enum npctype_e{bigdurno, littledurno,maxnpctype};

struct NPC_s {
  struct EntityLocation_s location;
  enum sprite_e idlesprite;
  enum sprite_e sprite;
  enum sprite_e weaponspritestart;
  enum sprite_e weaponspriteend;
  enum npctype_e npctype;
  struct EntityLocation_s velocity;
  int health;
  struct EntityLocation_s hitbox_bl;
  struct EntityLocation_s hitbox_tr;
  int collideforce;
  int collidable;
  int explodenextframe;
  int nextfire;
  int fireinterval;
  int firewidth;
};

struct Star_s {
  struct EntityLocation_s location;
  unsigned char length;
};

struct Player_s {
  struct EntityLocation_s location;
  enum sprite_e idlesprite;
  enum sprite_e sprite;
  unsigned char velocity;
  int nextidlesprite;
  int shields;
  int nextshieldheal;
  int integrity;
  int remainingdistance;
  struct EntityLocation_s hitbox_bl;
  struct EntityLocation_s hitbox_tr;
  struct EntityLocation_s phaser1;
  struct EntityLocation_s phaser2;
  int targetleft;
  int targetright;
  short int firingleft;
  short int firingright;
  int phaserdamage;
};

struct Projectile_s {
  struct EntityLocation_s location;
  enum sprite_e sprite;
  enum sprite_e spritestart;
  enum sprite_e spriteend;
  struct EntityLocation_s velocity;
  int collidable;
  int nextframe;
  short int active;
  int damage;
};

struct Star_s Stars[MAX_STARS];
struct NPC_s NPCS[MAX_NPCS];
struct Projectile_s Projectiles[MAX_PROJECTILES];
struct Player_s Player;

extern void game_respawn_npc(int id);
extern int tick;
extern int lasttick;
unsigned char tractorstart = 0;
unsigned char hailstart = 0;
enum debugs_e{dbbase,dbhitbox,dbweapons,dbinput,dbperformance,dbnpcs,dbprojectiles,dbmax};
int debugs[dbmax];

enum colour_e{lcars_black,lcars_violet1,debugpink,stargrey,debuggreen,phaserorange1,phaserorange2,tractor1,tractor2,tractor3,tractormax};
int colours[] = {0x11111100,0xc4727200,0xcc00ff00,0x66666600,0x00ff0000,0x0053ff00,0x00d5ff00,0x2e120600,0x1c100a00,0x75250000,0xbf430a00};

void game_spawn_projectile(int id, int Px, int Py, int Vx, int Vy, enum sprite_e spritestart, enum sprite_e spriteend, int damage)
{
  int i;
  
  if(id < 0)
  {
    for(i = 0; i < MAX_PROJECTILES; i++)
    {
        if(Projectiles[i].active == 0)
          id = i;
    }
  }

  // If no velocity X specified we're targetting the player
  if(Vx == 0)
  {
    int V;
    int Xdistance = abs((Player.location.X + (Player.hitbox_tr.X/2)) - Px);
    int Ydistance = abs((Player.location.Y + (Player.hitbox_tr.Y/2)) - Py);
    int distance = sqrt((Xdistance^2) + (Ydistance^2));
    V = Vy;
    Vx = Xdistance / (distance / Vy);
    Vy = Ydistance / (distance / Vy);

    if(Player.location.X > Px)
      Vx = 0 - Vx;
  }

  // If it's too close to horizontal you can't dodge
  if(Py < (Player.location.X + 100))
    id = -1;
  
  //If no free IDs then we go without
  if(id >= 0)
  {
    Projectiles[id].location.X = Px;
    Projectiles[id].location.Y = Py;
    Projectiles[id].velocity.X = Vx;
    Projectiles[id].velocity.Y = Vy;
    Projectiles[id].active = 1;
    Projectiles[id].sprite = spritestart;
    Projectiles[id].spritestart = spritestart;
    Projectiles[id].spriteend = spriteend;
    Projectiles[id].nextframe = tick + 10;
    Projectiles[id].damage = damage;
    Projectiles[id].collidable = 1;

    // Alternate between channels
    if(lastnpcchannel == PCMCHANNEL_NPC1)
      lastnpcchannel = PCMCHANNEL_NPC2;
    else
      lastnpcchannel = PCMCHANNEL_NPC1;

    sound_pcm_playsample(lastnpcchannel,PCMSAMPLE_DURNOTORPEDO);
  }

}

void game_draw_player()
{
  draw_sprite(sprites[Player.sprite], Player.location.X, Player.location.Y);

  if(debugs[dbhitbox])
  {
    graphics_colour(colours[debugpink]);

    // Bounding box debug
    draw_rectangle(
      Player.location.X + Player.hitbox_bl.X,
      Player.location.Y + Player.hitbox_bl.Y,
      Player.location.X + Player.hitbox_tr.X,
      Player.location.Y + Player.hitbox_tr.Y
    );
  }

  if((Player.firingleft) && (Player.targetleft >= 0))
  {
    graphics_colour(colours[phaserorange1]);
    draw_line(
      Player.location.X + Player.phaser1.X,
      Player.location.Y + Player.phaser1.Y,
      NPCS[Player.targetleft].location.X + ((NPCS[Player.targetleft].hitbox_bl.X + NPCS[Player.targetleft].hitbox_tr.X)/2),
      NPCS[Player.targetleft].location.Y + NPCS[Player.targetleft].hitbox_bl.Y
    );
    graphics_colour(colours[phaserorange2]);
    draw_dotted_line(
      Player.location.X + Player.phaser1.X + 1,
      Player.location.Y + Player.phaser1.Y,
      NPCS[Player.targetleft].location.X + ((NPCS[Player.targetleft].hitbox_bl.X + NPCS[Player.targetleft].hitbox_tr.X)/2) + 1,
      NPCS[Player.targetleft].location.Y + NPCS[Player.targetleft].hitbox_bl.Y
    );
    draw_dotted_line(
      Player.location.X + Player.phaser1.X - 1,
      Player.location.Y + Player.phaser1.Y,
      NPCS[Player.targetleft].location.X + ((NPCS[Player.targetleft].hitbox_bl.X + NPCS[Player.targetleft].hitbox_tr.X)/2) - 1,
      NPCS[Player.targetleft].location.Y + NPCS[Player.targetleft].hitbox_bl.Y
    );
    sound_pcm_playsample_ifidle(PCMCHANNEL_PLAYER,PCMSAMPLE_PHASER);
  }

  if((Player.firingright) && (Player.targetright >= 0))
  {
    graphics_colour(colours[phaserorange1]);
    draw_line(
      Player.location.X + Player.phaser2.X,
      Player.location.Y + Player.phaser2.Y,
      NPCS[Player.targetright].location.X + ((NPCS[Player.targetright].hitbox_bl.X + NPCS[Player.targetright].hitbox_tr.X)/2),
      NPCS[Player.targetright].location.Y + NPCS[Player.targetright].hitbox_bl.Y
    );
    graphics_colour(colours[phaserorange2]);
    draw_dotted_line(
      Player.location.X + Player.phaser2.X + 1,
      Player.location.Y + Player.phaser2.Y,
      NPCS[Player.targetright].location.X + ((NPCS[Player.targetright].hitbox_bl.X + NPCS[Player.targetright].hitbox_tr.X)/2) + 1,
      NPCS[Player.targetright].location.Y + NPCS[Player.targetright].hitbox_bl.Y
    );
    draw_dotted_line(
      Player.location.X + Player.phaser2.X - 1,
      Player.location.Y + Player.phaser2.Y,
      NPCS[Player.targetright].location.X + ((NPCS[Player.targetright].hitbox_bl.X + NPCS[Player.targetright].hitbox_tr.X)/2) - 1,
      NPCS[Player.targetright].location.Y + NPCS[Player.targetright].hitbox_bl.Y
    );
    sound_pcm_playsample_ifidle(PCMCHANNEL_PLAYER,PCMSAMPLE_PHASER);
  }

  if(debugs[dbweapons])
  {
    graphics_colour(colours[debuggreen]);

    // Draw phaser banks
    draw_line(
      Player.location.X + Player.phaser1.X,
      Player.location.Y + Player.phaser1.Y -5,
      Player.location.X + Player.phaser1.X,
      Player.location.Y + Player.phaser1.Y + 5
    );
    draw_line(
      Player.location.X + Player.phaser1.X -5,
      Player.location.Y + Player.phaser1.Y,
      Player.location.X + Player.phaser1.X +5,
      Player.location.Y + Player.phaser1.Y 
    );
    draw_line(
      Player.location.X + Player.phaser2.X,
      Player.location.Y + Player.phaser2.Y -5,
      Player.location.X + Player.phaser2.X,
      Player.location.Y + Player.phaser2.Y + 5
    );
    draw_line(
      Player.location.X + Player.phaser2.X -5,
      Player.location.Y + Player.phaser2.Y,
      Player.location.X + Player.phaser2.X +5,
      Player.location.Y + Player.phaser2.Y 
    );
  }
}

void game_draw_npcs()
{
  int i;
  for(i = 0; i < MAX_NPCS; i++)
  {
    // Don't bother if they are off the screen
    if(NPCS[i].location.Y < DISPLAY_Y)
    {
      draw_sprite(sprites[NPCS[i].sprite], NPCS[i].location.X, NPCS[i].location.Y);

      // Next sprite up in index is the appropriate target icon
      if(Player.targetleft == i)
        draw_sprite(sprites[NPCS[i].sprite + 1], NPCS[i].location.X, NPCS[i].location.Y);
      if(Player.targetright == i)
        draw_sprite(sprites[NPCS[i].sprite + 1], NPCS[i].location.X, NPCS[i].location.Y);

      if(tick > NPCS[i].explodenextframe)
      {
        if((NPCS[i].sprite >= explode_start) && (NPCS[i].sprite <= explode_end))
        {
          NPCS[i].sprite++;
          if(NPCS[i].sprite > explode_end)
            game_respawn_npc(i);
        }
        NPCS[i].explodenextframe = tick + 4;
      }
      
      if(debugs[dbhitbox])
      {
        graphics_colour(colours[debugpink]);

        // Bounding box debug
        draw_rectangle(
          NPCS[i].location.X + NPCS[i].hitbox_bl.X,
          NPCS[i].location.Y + NPCS[i].hitbox_bl.Y,
          NPCS[i].location.X + NPCS[i].hitbox_tr.X,
          NPCS[i].location.Y + NPCS[i].hitbox_tr.Y
        );
      }
    }
  }
}

void game_draw_projectiles()
{
  int i;
  for(i = 0; i < MAX_PROJECTILES; i++)
  {
    if(Projectiles[i].active == 0)
      continue;

    draw_sprite(sprites[Projectiles[i].sprite], Projectiles[i].location.X,Projectiles[i].location.Y);
    Projectiles[i].sprite++;
    if(Projectiles[i].sprite > Projectiles[i].spriteend)
      Projectiles[i].sprite = Projectiles[i].spritestart;
  }
}

void game_draw_stars()
{
  unsigned char i;
  graphics_colour(colours[stargrey]);
  for(i = 0; i < MAX_STARS; i++)
  {
    draw_line(Stars[i].location.X,Stars[i].location.Y,Stars[i].location.X,Stars[i].location.Y+Stars[i].length);
  }
}

void game_setup_player()
{
  Player.location.X = DISPLAY_X/2;
  Player.location.Y = PLAYER_Y_START;
  Player.sprite = player_ship;
  Player.idlesprite = player_ship;
  Player.velocity = 100;
  Player.shields = 100;
  Player.integrity = 100;
  Player.remainingdistance = 1500000;
  Player.hitbox_bl.X = 0;
  Player.hitbox_bl.Y = 0;
  Player.hitbox_tr.X = 60;
  Player.hitbox_tr.Y = 81;
  Player.phaser1.X = 20;
  Player.phaser1.Y = 75;
  Player.phaser2.X = 41;
  Player.phaser2.Y = 75;
  Player.targetleft = -1;
  Player.targetright = -1;
  Player.firingleft = 0;
  Player.firingright = 0;
  Player.phaserdamage = 10;
}

void game_tick_stars()
{
  int i;
  for(i = 0; i < MAX_STARS; i++)
  {
    Stars[i].location.Y -= (tick - lasttick) * 4;
    if((Stars[i].location.Y + Stars[i].length) <= 0)
    {
      Stars[i].length = 20 + (rand() % 10);
      Stars[i].location.X = rand() % DISPLAY_X;
      Stars[i].location.Y = DISPLAY_Y + Stars[i].length;
    }
  }
}

void game_setup_stars()
{
  int i;
  for(i = 0; i < MAX_STARS; i++)
  {
    Stars[i].length = 20 + (rand() % 10);
    Stars[i].location.X = rand() % DISPLAY_X;
    Stars[i].location.Y = rand() % DISPLAY_Y;
  }
}

void game_setup_input()
{
  /*
    inreg.r[0] = 4;
  inreg.r[1] = 1;
  _kernel_swi(OS_Byte,&inreg,&outreg);
  */
}

void game_respawn_npc(int id)
{
  if(Player.remainingdistance <= 120000)
    return;
  NPCS[id].location.X = rand() % DISPLAY_X;
  NPCS[id].location.Y = DISPLAY_Y + (rand() % (DISPLAY_Y/2));
  if((rand() % 10) > 3)
    NPCS[id].npctype = littledurno;
  else
    NPCS[id].npctype = bigdurno;
  NPCS[id].collidable = 1;
  NPCS[id].nextfire = tick + (rand() % 100);
    switch(NPCS[id].npctype)
    {
      case bigdurno:
        NPCS[id].idlesprite = durno_ship;
        NPCS[id].sprite = durno_ship;
        NPCS[id].velocity.X = 0;
        NPCS[id].velocity.Y = (rand() % 6) + 1;
        NPCS[id].health = 1000;
        NPCS[id].hitbox_bl.X = 0;
        NPCS[id].hitbox_bl.Y = 0;
        NPCS[id].hitbox_tr.X = 48;
        NPCS[id].hitbox_tr.Y = 74;
        NPCS[id].collideforce = 1000;
        NPCS[id].weaponspritestart = photon1;
        NPCS[id].weaponspriteend = photon2;
        NPCS[id].fireinterval = 100;
        NPCS[id].firewidth = 400;
        break;
      case littledurno:
        NPCS[id].idlesprite = durno_ship2;
        NPCS[id].sprite = durno_ship2;
        NPCS[id].velocity.X = (rand() % 4) + 2;
        NPCS[id].velocity.Y = (rand() % 4) + 2;
        NPCS[id].health = 300;
        NPCS[id].hitbox_bl.X = 0;
        NPCS[id].hitbox_bl.Y = 0;
        NPCS[id].hitbox_tr.X = 38;
        NPCS[id].hitbox_tr.Y = 56;
        NPCS[id].collideforce = 50;
        NPCS[id].weaponspritestart = plasma1;
        NPCS[id].weaponspriteend = plasma2;
        NPCS[id].fireinterval = 80;
        NPCS[id].firewidth = 30;
        break;
    };
}

void game_setup_npcs()
{
  int i;
  for(i = 0; i < MAX_NPCS; i++)
  {
    game_respawn_npc(i);
    NPCS[i].location.Y += 400;
  }
}

void game_tick_npcs()
{
  int i;
  for(i = 0; i < MAX_NPCS; i++)
  {
    NPCS[i].location.Y -= (tick - lasttick) * NPCS[i].velocity.Y;
    NPCS[i].location.X -= (tick - lasttick) * NPCS[i].velocity.X;

    if(NPCS[i].npctype == littledurno)
    {
      if(NPCS[i].location.Y > (Player.location.Y + 500))
      {
        if(abs(NPCS[i].location.X - Player.location.X) > 100)
        {
          if(NPCS[i].location.X < Player.location.X)
            NPCS[i].velocity.X = 0 - abs(NPCS[i].velocity.X);
          else
            NPCS[i].velocity.X = abs(NPCS[i].velocity.X);
        }
      }else{
        if(NPCS[i].velocity.X > 0)
         NPCS[i].velocity.X = 1;
        else
         NPCS[i].velocity.X = -1;

        // Make the accelerate if they're close
        NPCS[i].location.Y -= (tick - lasttick) * NPCS[i].velocity.Y;
      }
    }

    if(NPCS[i].location.Y + NPCS[i].hitbox_tr.Y <= 0)
      game_respawn_npc(i);
    
    if((NPCS[i].location.X + NPCS[i].hitbox_tr.X) > DISPLAY_X)
      game_respawn_npc(i);
    
    if((NPCS[i].location.X + NPCS[i].hitbox_bl.X) < 0)
      game_respawn_npc(i);

    if(tick > NPCS[i].nextfire)
    {
      // Dont fire if off screen
      if((NPCS[i].location.Y < DISPLAY_Y))
      {
        if((NPCS[i].firewidth == -1) || (abs(Player.location.X - NPCS[i].location.X) < NPCS[i].firewidth))
        {
          if(NPCS[i].weaponspritestart == photon1)
            game_spawn_projectile(-1,NPCS[i].location.X, NPCS[i].location.Y, 0, 10, NPCS[i].weaponspritestart, NPCS[i].weaponspriteend, 60);
          else
            game_spawn_projectile(-1,NPCS[i].location.X, NPCS[i].location.Y, 0, 10, NPCS[i].weaponspritestart, NPCS[i].weaponspriteend, 20);
          NPCS[i].nextfire = tick + NPCS[i].fireinterval;
        }
      }
    }

    if(NPCS[i].health <= 0)
    {
      if(NPCS[i].sprite == NPCS[i].idlesprite)
      {
        NPCS[i].collidable = 0;
        NPCS[i].sprite = explode_start;
        NPCS[i].explodenextframe = tick + 4;
        sound_pcm_playsample(PCMCHANNEL_AMBIENT,PCMSAMPLE_SHIPEXPLODE);
      }
    }
  }
}

void game_draw_debugmenu()
{
/*  int i;
  if(debugs[dbbase])
  {
    font_colour(colours[debuggreen],colours[lcars_black],font[sys_12_8]);
    draw_text("Debug List",DISPLAY_X-200,DISPLAY_Y-40,font[sys_12_8]);
    font_colour(colours[debugs[dbhitbox]?debugpink:stargrey],colours[lcars_black],font[sys_12_8]);
    draw_text("1: hitbox",DISPLAY_X-200,DISPLAY_Y-60,font[sys_12_8]);
    font_colour(colours[debugs[dbweapons]?debugpink:stargrey],colours[lcars_black],font[sys_12_8]);
    draw_text("2: weapons",DISPLAY_X-200,DISPLAY_Y-80,font[sys_12_8]);
    font_colour(colours[debugs[dbinput]?debugpink:stargrey],colours[lcars_black],font[sys_12_8]);
    draw_text("3: input",DISPLAY_X-200,DISPLAY_Y-100,font[sys_12_8]);
    font_colour(colours[debugs[dbperformance]?debugpink:stargrey],colours[lcars_black],font[sys_12_8]);
    draw_text("4: performance",DISPLAY_X-200,DISPLAY_Y-120,font[sys_12_8]);
    font_colour(colours[debugs[dbnpcs]?debugpink:stargrey],colours[lcars_black],font[sys_12_8]);
    draw_text("5: NPCs",DISPLAY_X-200,DISPLAY_Y-140,font[sys_12_8]);
    font_colour(colours[debugs[dbprojectiles]?debugpink:stargrey],colours[lcars_black],font[sys_12_8]);
    draw_text("6: Projectiles",DISPLAY_X-200,DISPLAY_Y-160,font[sys_12_8]);
  }

  if(debugs[dbperformance])
  {
    font_colour(colours[debuggreen],colours[lcars_black],font[sys_12_8]);
    sprintf(hudbuffer,"CPF: %i",(tick-lasttick));
    draw_text(hudbuffer,DISPLAY_X-500,DISPLAY_Y-60,font[sys_12_8]);
  }

  if(debugs[dbnpcs])
  {
    font_colour(colours[debuggreen],colours[lcars_black],font[sys_12_8]);
    for(i = 0; i < MAX_NPCS; i++)
    {
      sprintf(hudbuffer,"NPCS[%i] %i%i,%i %i,%i %i",i,NPCS[i].health,NPCS[i].velocity.X,NPCS[i].velocity.Y,NPCS[i].location.X,NPCS[i].location.Y);
      draw_text(hudbuffer,DISPLAY_X-800,DISPLAY_Y-60-(i * 20),font[sys_12_8]);
    }
  }

  if(debugs[dbprojectiles])
  {
    font_colour(colours[debuggreen],colours[lcars_black],font[sys_12_8]);
    for(i = 0; i < MAX_PROJECTILES; i++)
    {
      sprintf(hudbuffer,"Projectiles[%i] %i %i,%i %i,%i",i,Projectiles[i].active,Projectiles[i].velocity.X,Projectiles[i].velocity.Y,Projectiles[i].location.X,Projectiles[i].location.Y);
      draw_text(hudbuffer,DISPLAY_X-800,DISPLAY_Y-60-(i * 20),font[sys_12_8]);
    }
  }*/
}

void game_tick_collider()
{
  int i;
  for(i = 0; i < MAX_NPCS; i++)
  {
    if(!NPCS[i].collidable)
      continue;
    // Excuse this formatting
    if(
      game_hitbox_collide(
        (Player.location.X + Player.hitbox_bl.X),(Player.location.Y + Player.hitbox_bl.Y),
        (Player.hitbox_tr.X - Player.hitbox_bl.X),(Player.hitbox_tr.Y - Player.hitbox_bl.Y),
        (NPCS[i].location.X + NPCS[i].hitbox_bl.X),(NPCS[i].location.Y + NPCS[i].hitbox_bl.Y),
        (NPCS[i].hitbox_tr.X - NPCS[i].hitbox_bl.X),(NPCS[i].hitbox_tr.Y - NPCS[i].hitbox_bl.Y)
      )
    )
    {

      /*if(debugs[dbhitbox])
      {
        font_colour(colours[debuggreen],colours[lcars_black],font[sys_12_8]);
        sprintf(hudbuffer,"NPCS[%i] hits player",i,NPCS[i].location.X,NPCS[i].location.Y);
        draw_text(hudbuffer,DISPLAY_X-900,DISPLAY_Y-260-(i * 20),font[sys_12_8]);
      }*/

      NPCS[i].health -= 300;
      NPCS[i].collidable = 0;
      NPCS[i].velocity.Y = NPCS[i].velocity.Y / 2;
      NPCS[i].velocity.X = NPCS[i].velocity.X * 4;

      Player.shields -= NPCS[i].collideforce;
      if(Player.shields < 0)
      {
        Player.integrity += Player.shields;
        Player.shields = 0;
      }
    }
  }

  for(i = 0; i < MAX_PROJECTILES; i++)
  {
    if(!Projectiles[i].active)
      continue;
    if(!Projectiles[i].collidable)
      continue;
    // Excuse this formatting
    if(
      game_hitbox_collide(
        (Player.location.X + Player.hitbox_bl.X),(Player.location.Y + Player.hitbox_bl.Y),
        (Player.hitbox_tr.X - Player.hitbox_bl.X),(Player.hitbox_tr.Y - Player.hitbox_bl.Y),
        Projectiles[i].location.X,Projectiles[i].location.Y,
        10,10
      )
    )
    {
      sound_play(2,-5, 20,1);
      Projectiles[i].active = 0;
      Player.shields -= Projectiles[i].damage;
      if(Player.shields < 0)
      {
        Player.integrity += Player.shields;
        Player.shields = 0;
      }
    }
  }
}

void game_tick_player_targets()
{
  int i;
  int NoseX = Player.location.X + Player.hitbox_bl.X + (Player.hitbox_tr.X/2);
  int NoseXLeft = Player.location.X + Player.hitbox_bl.X;
  int NoseXRight = Player.location.X + Player.hitbox_bl.X  + Player.hitbox_tr.X;
  int NoseY = Player.location.Y + + Player.hitbox_bl.Y  + Player.hitbox_tr.Y;
  int LeftDistance = 1000;
  int RightDistance = 1000;
  int LeftCornerX, LeftCornerY, RightCornerX,DistanceX,DistanceY;
  Player.targetleft = -1;
  Player.targetright = -1;

  for(i = 0; i < MAX_NPCS; i++)
  {
    if(!NPCS[i].collidable)
      continue;
    if(NPCS[i].location.Y > (DISPLAY_Y-50))
      continue;
    LeftCornerX = NPCS[i].location.X + NPCS[i].hitbox_bl.X;
    LeftCornerY = NPCS[i].location.Y + NPCS[i].hitbox_bl.Y;
    RightCornerX = NPCS[i].location.X + NPCS[i].hitbox_tr.X;

    if(LeftCornerY > NoseY)
    {
      DistanceY = LeftCornerY - NoseY;
      DistanceX = abs(NoseX - ((LeftCornerX + RightCornerX) / 2));

      if((DistanceY/5) > DistanceX)
      {
        if((NoseXRight - ((LeftCornerX + RightCornerX) / 2)) > 0)
        {
          if(DistanceY < LeftDistance)
          {
            LeftDistance = DistanceY;
            Player.targetleft = i;
          }
        }
        if((NoseXLeft - ((LeftCornerX + RightCornerX) / 2)) < 0)
        {
          if(DistanceY < RightDistance)
          {
            RightDistance = DistanceY;
            Player.targetright = i;
          }
        }
      }
    }
  }

}

void game_tick_projectiles()
{
  int i;
  for(i = 0; i < MAX_PROJECTILES; i++)
  {
    if(!Projectiles[i].active)
      continue;

    if(tick > Projectiles[i].nextframe)
    {
      if((Projectiles[i].sprite >= photon1) && (Projectiles[i].sprite <= photon2))
      {
        Projectiles[i].sprite++;
        if(Projectiles[i].sprite > photon2)
          Projectiles[i].sprite = photon1;
      }
      Projectiles[i].nextframe = tick + 10;
    }


    Projectiles[i].location.Y -= (tick - lasttick) * Projectiles[i].velocity.Y;
    Projectiles[i].location.X -= (tick - lasttick) * Projectiles[i].velocity.X;

    if((Projectiles[i].location.Y) <= 0)
    {
      Projectiles[i].active = 0;
    }
  }
}



void game_tick_input()
{
  if(debugs[dbbase])
  {
    // 1
    if(input_readkey(17))
      debugs[dbinput] = 1;
    // 2
    if(input_readkey(48))
      debugs[dbhitbox] = 1;
    // 3
    if(input_readkey(49))
      debugs[dbweapons] = 1;
    // 4
    if(input_readkey(18))
      debugs[dbperformance] = 1;
    // 5
    if(input_readkey(19))
      debugs[dbnpcs] = 1;
    // 6
    if(input_readkey(24))
      debugs[dbprojectiles] = 1;
  }
  
  /*if(debugs[dbinput])
  {
    font_colour(colours[debuggreen],colours[lcars_black],font[sys_12_8]);
    sprintf(hudbuffer,"Keycode: %i",input_readanykey());
    draw_text(hudbuffer,DISPLAY_X-500,DISPLAY_Y-40,font[sys_12_8]);
  }*/

  // Q
  if(input_readkey(16))
    Player.remainingdistance = 0;
//    debugs[dbbase] = 1;



  // E
  if(input_readkey(33))
  {
    Player.remainingdistance = 120000;
  }

  // Right arrow
  if(input_readkey(121))
  {
    Player.location.X += PLAYER_X_SPEED * (tick - lasttick);
    Player.sprite = player_shipr;
    Player.nextidlesprite = tick + 15;
    if((Player.location.X + Player.hitbox_tr.X) > DISPLAY_X)
    {
      Player.location.X = DISPLAY_X - Player.hitbox_tr.X;
    }
  }

  // Left arrow
  if(input_readkey(25))
  {
    Player.location.X -= PLAYER_X_SPEED * (tick - lasttick);
    Player.sprite = player_shipl;
    Player.nextidlesprite = tick + 15;
    if((Player.location.X + Player.hitbox_bl.X) < 0)
    {
      Player.location.X = 0 - Player.hitbox_bl.X;
    }
  }

  // Space
  if(input_readkey(98))
  {
    Player.firingleft = 1;
    Player.firingright = 1;
    Player.nextshieldheal = tick + 100;
  }else{
    Player.firingleft = 0;
    Player.firingright = 0;
  }
}

void game_setup_audio()
{
  sound_set_voice(1,"WaveSynth-Beep");
  sound_set_voice(2,"Percussion-Noise");
  sound_set_voice(3,"Percussion-Soft");

  sound_pcm_loadsample(PCMSAMPLE_DURNOTORPEDO,"sounds.torpedo");
  sound_pcm_loadsample(PCMSAMPLE_PHASER,"sounds.phaser");
  sound_pcm_loadsample(PCMSAMPLE_SHIPEXPLODE,"sounds.shpexp");
  sound_pcm_loadsample(PCMSAMPLE_TRACTOR,"sounds.tractor");
  sound_pcm_loadsample(PCMSAMPLE_HAIL,"sounds.hail");
}
void game_draw_hud()
{
  draw_sprite("lcars",4,DISPLAY_Y-164);
  draw_sprite("pointer",141+(Player.shields*2),DISPLAY_Y - 164 + 84);
  draw_sprite("pointer",141+(Player.integrity*2),DISPLAY_Y - 164 + 44);
  draw_sprite("pointer",141+(Player.remainingdistance/7500),DISPLAY_Y - 164 + 4);
}

void game1_setup()
{
  game_setup_input();
  game_setup_audio();
  game_setup_stars();
  game_setup_player();
  game_setup_npcs();
  tick = clock();
}

void game_tick_player()
{
  if(tick > Player.nextidlesprite)
    Player.sprite = Player.idlesprite;

  Player.remainingdistance -= Player.velocity * (tick - lasttick);

  if(Player.shields < 100)
  {
    if(tick > Player.nextshieldheal)
    {
      Player.shields += 1;
      Player.nextshieldheal = tick + 10;
    }
  }

  if((Player.firingleft) && (Player.targetleft >= 0))
  {
    NPCS[Player.targetleft].health -= (tick - lasttick) * Player.phaserdamage;
  }

  if((Player.firingright) && (Player.targetright >= 0))
  {
    NPCS[Player.targetright].health -= (tick - lasttick) * Player.phaserdamage;
  }
}

void game1_death()
{
  int currentstart = 0;
  int introframe = 0;

  sound_set_voice(1,"WaveSynth-Beep");
  sound_set_voice(2,"WaveSynth-Beep");
  sound_set_voice(3,"WaveSynth-Beep");
  sound_set_voice(4,"WaveSynth-Beep");
  sound_composition_init();

  sound_composition_load("music.cmpagrac");

  tick = clock();

  sound_composition_start(clock());

  draw_sprite("spacebar",(DISPLAY_X/2)-106,50);
  draw_sprite("kia",(DISPLAY_X/2)-300,500);

  screen_flipbuffer();

  while(sound_composition_incomplete())
  {
    sound_composition_tick(clock());

      if(clock() > (tick + 100))
      {
        if(input_readkey(98))
          sound_composition_stop();
      }
  }
}

void game_tick_npcs_scatter()
{
  int i;
  for(i = 0; i < MAX_NPCS; i++)
  {
    NPCS[i].location.Y -= (tick - lasttick) * NPCS[i].velocity.Y;
    if(NPCS[i].velocity.X == 0)
    {
      if(NPCS[i].location.X > Player.location.X)
        NPCS[i].velocity.X = 1;
      else
        NPCS[i].velocity.X = -1;
    }
    NPCS[i].location.X += (tick - lasttick) * ((NPCS[i].velocity.X * 3));
  
    if(NPCS[i].health <= 0)
    {
      if(NPCS[i].sprite == NPCS[i].idlesprite)
      {
        NPCS[i].collidable = 0;
        NPCS[i].sprite = explode_start;
        NPCS[i].explodenextframe = tick + 4;
        sound_pcm_playsample_ifidle(PCMCHANNEL_AMBIENT,PCMSAMPLE_SHIPEXPLODE);
      }
    }
  }
}

void game_draw_tractor()
{
  int tractorx;
  int colour;

  Player.location.Y += PLAYER_X_SPEED * (tick - lasttick) / 6;

  for(tractorx = 0; tractorx <= (Player.hitbox_tr.X - Player.hitbox_bl.X); tractorx = tractorx + 4)
  {
      colour = tractor1;
      colour += rand() % (tractormax - tractor1);
      graphics_colour(colours[colour]);
      if(tractorx % 2 == 0)
      {
        draw_line(
        (Player.location.X + Player.hitbox_bl.X) + tractorx,
        Player.location.Y + (Player.hitbox_tr.Y/2),
        DISPLAY_X/2,
        DISPLAY_Y + 500
        );  
      }else{
        draw_dotted_line(
        (Player.location.X + Player.hitbox_bl.X) + tractorx,
        Player.location.Y + (Player.hitbox_tr.Y/2),
        DISPLAY_X/2,
        DISPLAY_Y + 200
        );
      }
  }
}

void game_tick_endingcentre()
{
  if((Player.location.X + Player.hitbox_bl.X + (Player.hitbox_tr.X/2)) < ((DISPLAY_X/2)-50))
  {
    Player.location.X += PLAYER_X_SPEED * (tick - lasttick);
    Player.sprite = player_shipr;
    Player.nextidlesprite = tick + 15;
  }
  else if((Player.location.X + Player.hitbox_bl.X + (Player.hitbox_tr.X/2)) > ((DISPLAY_X/2)+50))
  {
    Player.location.X -= PLAYER_X_SPEED * (tick - lasttick);
    Player.sprite = player_shipr;
    Player.nextidlesprite = tick + 15;
  }
  else
  {
    Player.location.X = (DISPLAY_X/2);
  }
}

void game1_briefing()
{
  screen_clear();

  draw_spritetext(
"~~~~~~~~{ chief engineers log - uss archimedes - stardate 1234567890 ==}\n\n\nthe federation has begun diplomatic relations with the planet \nkorell. their unusual position within an asteroid belt means \ntheir planet has to be protected by a deflector shield. the \nfederation has offered to help enhance this shield. once this \nupgrade was completed we were preparing to leave when we \nrecieved a priority one transmission from the archimedes \nsaying a fleet of fighters from the neighbouring planet of \ndurnovaria was en route to korell. it transpires that the planet's \nshield may also have some military value that was concealed \nfrom the federation. the durnovarians have said our shuttle is \nnow considered a military target and will be treated as such. \nwe must to get back to the archimedes before we get dragged \nfurther into the conflict between the two planets.\n\n\n\n\n\n\n\n~~~~~~~~{======================~~~~~~~~===========================}"
, 50, 950);

  draw_sprite("spacebar",(DISPLAY_X/2)-106,50);

  screen_flipbuffer();

  tick = clock();
  while(1)
  {
    if(clock() > (tick + 100))
    {
      if(input_readkey(98))
        return;
    }
  }
}

void game1_victory()
{
  screen_clear();

  draw_spritetext(
"~~~~~~~~{ chief engineers log - uss archimedes - stardate 1234567890 ==}\n\n\nour shuttlepod has been rescued by the uss archimedes - but our \njob here isn't done. the archimedes is now under attack from the \ndurnovarians, and recoverying the shuttle has come at a cost. \nseveral major systems are now offline.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n~~~~~~~~{======================~~~~~~~~===========================}"
, 50, 950);

  draw_sprite("spacebar",(DISPLAY_X/2)-106,50);

  screen_flipbuffer();

  tick = clock();
  while(1)
  {
    if(clock() > (tick + 100))
    {
      if(input_readkey(98))
        return;
    }
  }
}

void game_draw_comms(char* msg)
{
  // TODO: Need to scale this properly once font/text is final
  draw_sprite("commspan1",4,DISPLAY_Y-164);
  draw_spritetext(msg, 80, DISPLAY_Y-80);
}

int game1_tick()
{
  lasttick = tick;
  tick = clock();

  screen_flipbuffer();
  screen_clear();

  if(Player.remainingdistance <= 0)
  {
    return 2;
  }else if (Player.remainingdistance <= 120000)
  {
    if(Player.remainingdistance <= 80000)
    {
      PROFILE(game_draw_tractor());
      if(!tractorstart)
      {
        tractorstart = 1;
        sound_pcm_playsample(PCMCHANNEL_AMBIENT,PCMSAMPLE_TRACTOR);
      }
    }

    game_tick_endingcentre();
    // Run a smaller subset of the norm near the ending
    PROFILE(game_tick_stars());
    PROFILE(game_tick_player());
    PROFILE(game_tick_npcs_scatter());
    PROFILE(game_tick_projectiles());
    PROFILE(game_draw_stars());
    PROFILE(game_draw_player());
    PROFILE(game_draw_npcs());
    PROFILE(game_draw_projectiles());
    if(!hailstart)
    {
      hailstart = 1;
      sound_pcm_playsample(PCMCHANNEL_AMBIENT,PCMSAMPLE_HAIL);
    }
    game_draw_comms("shuttlepod phoebe - this is the USS Archimedes.\npower down your engines and stand by for\nimmediate tractor beam recovery.");
    return 0;
  }
  else if(Player.integrity > 0)
  {
    PROFILE(game_tick_stars());
    PROFILE(game_tick_input());
    PROFILE(game_tick_player());
    PROFILE(game_tick_npcs());
    PROFILE(game_tick_collider());
    PROFILE(game_tick_player_targets());
    PROFILE(game_tick_projectiles());
    PROFILE(game_draw_stars());
    PROFILE(game_draw_player());
    PROFILE(game_draw_npcs());
    PROFILE(game_draw_projectiles());

    PROFILE(game_draw_hud());
    PROFILE(game_draw_debugmenu());
    return 0;
  }else{
    screen_flipbuffer();
    screen_clear();
    game1_death();
    return 1;
  }
  

}
