/****************************************************************************/
/**                                                                        **/
/**                                 cch.c                                  **/
/**                                                                        **/
/** ColecoVision Cosmo Challenge game main module                          **/
/**                                                                        **/
/** Copyright (C) Marcel de Kogel 1997                                     **/
/**     You are not allowed to distribute this software commercially       **/
/**     Please, notify me, if you make any changes to this file            **/
/****************************************************************************/

#include <coleco.h>
#include <getput1.h>
#include <string.h>

/* VDP table addresses */
#define chrtab  0x1800
#define chrgen  0x0000
#define coltab  0x2000
#define sprtab  0x3800
#define sprgen  0x1b00

extern const sound_t snd_table[];

/* various RLE-encoded tables (in cch_tabl.c) */
extern const byte title_screen[];
extern const byte intro_screen[];
extern const byte stone_characters[];
extern const byte sprite_patterns[];

/* when his health bar reaches 0, one dies */
static byte player1_health,player2_health;
/* incremented every interrupt */
static byte nmi_count;
/* if 1, the title and intro screens have been displayed */
static byte game_running;
/* 0=one player, 1=two players */
static byte game_mode;

/* scrolling stones buffer */
static byte stone_vram[96*2];
/* next stone type to put in buffer */
static byte stonea,stoneb;

/* scroll stone stuff one character */
static void scroll_stones (void)
{
 register byte a;
 /* scroll old buffer */
 memcpyb (stone_vram,stone_vram+1,95);
 memcpyf (stone_vram+97,stone_vram+96,95);
 /* check new stones */
 if (!stonea)
 {
  a=get_random();
  if (a<16) stonea=((a&3)<<2)+128+16+32;
 }
 if (!stoneb)
 {
  a=get_random();
  if (a<16) stoneb=((a&3)<<2)+3+32;
 }
 /* put new stones in buffer */
 if (stonea)
 {
  stone_vram[31]=stonea-32;
  stone_vram[31+32]=stonea;
  stone_vram[31+64]=stonea+32;
  stonea++;
  if ((stonea&3)==0) stonea=0;
 }
 else
  stone_vram[31]=stone_vram[31+32]=stone_vram[31+64]=96;
 if (stoneb)
 {
  stone_vram[96]=stoneb-32;
  stone_vram[128]=stoneb;
  stone_vram[160]=stoneb+32;
  stoneb--;
  if ((stoneb&3)==3) stoneb=0;
 }
 else
  stone_vram[96]=stone_vram[128]=stone_vram[160]=96;
}

/* The NMI routine. Gets called 50 or 60 times per second */
void nmi (void)
{
 static byte stone_count;
 register byte i;
 nmi_count++;
 /* if intro screen is being displayed, return immediately */
 if (!game_running) return;
 /* Update sprites */
 update_sprites (32-8,sprgen+8*4);
 /* update stone display */
 switch (stone_count)
 {
  case 6:
   /* scroll 2 pixels */
   put_vram_ex (chrtab+256,stone_vram,96,0xff,128);
   put_vram_ex (chrtab+384,stone_vram+96,96,0xff,128);
   break;
  case 4:
   /* scroll 4 pixels */
   put_vram_ex (chrtab+256,stone_vram,96,0xff,16);
   put_vram_ex (chrtab+384,stone_vram+96,96,0xff,16);
   break;
  case 2:
   /* scroll 6 pixels */
   put_vram_ex (chrtab+256,stone_vram,96,0xff,128|16);
   put_vram_ex (chrtab+384,stone_vram+96,96,0xff,128|16);
   break;
  case 1:
   /* update buffer, will be uploaded next call */
   scroll_stones ();
   break;
  case 0:
   /* upload stone stuff to vram */
   put_vram (chrtab+256,stone_vram,96);
   put_vram (chrtab+384,stone_vram+96,96);
   stone_count=8;
   break;
 }
 stone_count--;
 /* update stars */
 if ((nmi_count&1)==0)
 {
  i=((nmi_count>>1)&7)+16;
  sprites[i].y=get_random();
  sprites[i].x=get_random();
  sprites[i].pattern=28+(get_random()&4);
  sprites[i].colour=get_random()&15;
 }
 /* Update sound */
 //update_sound ();
}

static const sprite_t sprites_init[]=
{
 /* ship A */
 { 166,60,4,15 },
 { 166,60,8,4 },
 { 166,60,12,8 },
 /* ship B */
 { 8,180,16,15 },
 { 8,180,20,4 },
 { 8,180,24,8 },
 /* 5 bullets each */
 { 207,0,0,11 },
 { 207,0,0,11 },
 { 207,0,0,11 },
 { 207,0,0,11 },
 { 207,0,0,11 },
 { 207,0,0,11 },
 { 207,0,0,11 },
 { 207,0,0,11 },
 { 207,0,0,11 },
 { 207,0,0,11 },
 /* 8 stars */
 { 207,0,28,14 },
 { 207,0,28,14 },
 { 207,0,28,14 },
 { 207,0,28,14 },
 { 207,0,28,14 },
 { 207,0,28,14 },
 { 207,0,28,14 },
 { 207,0,28,14 }
};

/* to prevent sprites being displayed on the status bar */
static const sprite_t default_sprgen[8]=
{
 { -9,0,0,0 },
 { -9,0,0,0 },
 { -9,0,0,0 },
 { -9,0,0,0 },
 { 183,0,0,0 },
 { 183,0,0,0 },
 { 183,0,0,0 },
 { 183,0,0,0 }
};

void show_player1_health_bar (byte n)
{
 register byte i;
 if (n && player1_health)
 {
  fill_vram (chrtab+736,1,player1_health);
  i=32-player1_health;
  if (i)
   fill_vram (chrtab+736+player1_health,2,i);
 }
 else
  fill_vram (chrtab+736,2,32);
}

void show_player2_health_bar (byte n)
{
 register byte i;
 if (n && player2_health)
 {
  fill_vram (chrtab,1,player2_health);
  i=32-player2_health;
  if (i)
   fill_vram (chrtab+player2_health,2,i);
 }
 else
  fill_vram (chrtab,2,32);
}

void player1_is_hit (int spritenr)
{
 sprites[spritenr].y=207;
 if (player1_health)
 {
     player1_health--;
     if (player1_health==4) {
         /* play sound low health */
         play_sound(1);
     }
     play_sound(3);
     /* play shound hit */
  show_player1_health_bar(1);
 }
}

void player2_is_hit (int spritenr)
{
 sprites[spritenr].y=207;
 if (player2_health)
 {
     player2_health--;
     if (player2_health==4) {
         /* play sound low health */
         play_sound(1);
     }
     play_sound(3);
     /* play shound hit */
  show_player2_health_bar(1);
 }
}

void stone_is_hit (int spritenr)
{
  sprites[spritenr].y=207;
  play_sound(4);
 //start_sound (block_sound,block_sound_priority);
}

byte check_stone (byte spritenr)
{
 register unsigned n;
 n=(sprites[spritenr].y)>>3;
 n-=8;
 if (n&0xf8) return 0;
 if (n==3 || n==7) return 0;
 if (n&4) --n;
 n<<=5;
 n+=(sprites[spritenr].x>>3);
 return stone_vram[n]!=96;
}

void _fill_vram (unsigned offset,byte value,unsigned count)
{
 disable_nmi ();
 fill_vram (offset,value,count);
 enable_nmi ();
}

static void title (void)
{
 /* initialise name table */
 screen_mode_2_bitmap();
 /* show title screen */
 show_picture (title_screen);
 pause();
 /* show intro screen */
 show_picture (intro_screen);
 pause();
}

static const byte red_font[8]= { 0x61,0x61,0x81,0x81,0x81,0x91,0x91,0x91 };
static const byte yellow_font[8]= { 0xa1,0xa1,0xb1,0xb1,0xb1,0xf1,0xf1,0xf1 };

void init_vdp (void)
{
 byte i;
 screen_mode_2_text();
 /* Disable Interrupt */
 disable_nmi();
 /* Turn off screen */
 screen_off();
 /* special color for the middle section */   
 vdp_out(3,0xbf); 
 /* Upload first 8 sprites */
 put_vram (sprgen,default_sprgen,sizeof(default_sprgen));
 /* Fill colour table */
 put_vram_pattern (coltab,yellow_font,8,256);
 put_vram_pattern (coltab+'0'*8,red_font,8,10);
 /* Clear Characters Pattern Table */
 fill_vram (chrgen,0x00,0x0800);
 /* Upload ASCII character patterns */
 upload_ascii (29,128-29,chrgen+29*8,BOLD_ITALIC);
 /* Health indicator */
 fill_vram (chrgen+9,0x55,6);
 fill_vram (coltab+8,0x90,16);
 /* Duplicate Charset to the other 2 screen section */
 duplicate_pattern();
 /* Upload stones character definitions */
 rle2vram (rle2vram(stone_characters,coltab+256*8),chrgen+256*8);
 /* Upload sprite patterns */
 rle2vram (sprite_patterns,sprtab);
 /* clear screen display */
 fill_vram (chrtab,32,768);
 /* Fill part 2 of name table */
 fill_vram (chrtab+256,96,256);
 /* Scroll in stones */
 for (i=32;i!=0;--i) scroll_stones ();
 /* Blue screen border */
 vdp_out (7,0xf4);
 /* turn display on */
 screen_on ();
 /* enable NMI */
 enable_nmi ();
}

void choice (void)
{
 _fill_vram (chrtab+0,1,32);
 _fill_vram (chrtab+736,1,32);
 _fill_vram (chrtab+18*32,0,64);
 center_string (18,"1 - One player ");
 center_string (19,"2 - Two players");
 /* wait until all keys released */
 while (keypad_1!=0xff || keypad_2!=0xff);
 /* get choice */
 game_mode=255;
 while (game_mode==255)
 {
  if (keypad_1==1 || keypad_2==1) game_mode=0;
  if (keypad_1==2 || keypad_2==2) game_mode=1;
 }
 _fill_vram (chrtab+18*32,0,64);
}

void main (void)
{
  byte i;
  byte old_joypad_1,old_joypad_2;
  set_snd_table(snd_table);
  title ();
  init_vdp ();
  /* set game_running flag, enables scrolling stones and blinking stars */
  game_running=1;
  /* clear NMI counter */
  nmi_count=255;
options:
  choice ();
play_again:
  player1_health=player2_health=32;
  show_player1_health_bar(1);
  show_player2_health_bar(1);
  memcpy (sprites,sprites_init,sizeof(sprites_init));
  old_joypad_1 = 8;
  old_joypad_2 = rnd_byte(0,1) == 0 ? 2 : 8;
  center_string (18,"Get ready");
  play_sound(9);
  play_sound(10);
  play_sound(11);
  delay (150);
  _fill_vram (chrtab+18*32,0,64);
  old_joypad_1=0;
  old_joypad_2=0;
  while (1)
  {
   /* Wait for a VBLANK */
   delay(1);
   if ((nmi_count&31)==0)
   {
    //start_sound (bg_sound,bg_sound_priority);
    if (player1_health<5 || player2_health<5)
    {
     if (player1_health<5) show_player1_health_bar(nmi_count&32);
     if (player2_health<5) show_player2_health_bar(nmi_count&32);
    }
   }
   /* Let computer play */
   if (!game_mode)
   {
    /* direction, slightly random */
    joypad_2=old_joypad_2&0x0f;
    i=get_random();
     if (i<24)
     {
         if (sprites[0].x<sprites[3].x)
         {
             joypad_2 = 8;
         }
         else
         {
             joypad_2 = 2;
         }
     }
    /* fire ? */
    if ((nmi_count&7)==0) joypad_2|=0x10;
   }
   /* Parse joysticks */
   if (joypad_1&2)
   {
    if (sprites[0].x<241)
     sprites[1].x=sprites[2].x=(++sprites[0].x);
   }
   if (joypad_1&8)
   {
    if (sprites[0].x)
     sprites[1].x=sprites[2].x=(--sprites[0].x);
   }
   if (joypad_2&2)
   {
    if (sprites[3].x<241)
     sprites[4].x=sprites[5].x=(++sprites[3].x);
   }
   if (joypad_2&8)
   {
    if (sprites[3].x)
     sprites[4].x=sprites[5].x=(--sprites[3].x);
   }
   if ((joypad_1^old_joypad_1)&joypad_1&0xf0)
   {
    for (i=6;i<11;++i) if (sprites[i].y==207) break;
    if (i<11)
    {
     sprites[i].y=sprites[0].y;
     sprites[i].x=sprites[0].x+7;
     play_sound(2);
     //start_sound (shoot_sound,shoot_sound_priority);
    }
   }
   if ((joypad_2^old_joypad_2)&joypad_2&0xf0)
   {
    for (i=11;i<16;++i) if (sprites[i].y==207) break;
    if (i<16)
    {
     sprites[i].y=sprites[3].y+14;
     sprites[i].x=sprites[3].x+7;
     play_sound(2);
     //start_sound (shoot_sound,shoot_sound_priority);
    }
   }
   old_joypad_1=joypad_1;
   old_joypad_2=joypad_2;
   if (player1_health && player2_health)
   {
    for (i=6;i<11;++i)
    {
     if (sprites[i].y!=207)
     {
      if (check_stone(i)) {
         stone_is_hit (i);
      }
      if (check_collision(sprites+i,sprites+3,0x303,0x303,0x1000,0x0e00)) {
         player2_is_hit (i);
      }
      if ((sprites[i].y-=4)>192) sprites[i].y=207;
     }
    }
    for (i=11;i<16;++i)
    {
     if (sprites[i].y!=207)
     {
      if (check_stone(i))
       stone_is_hit (i);
      if (check_collision(sprites+i,sprites+0,0x303,0x303,0x1000,0x0e02))
       player1_is_hit (i);
      if ((sprites[i].y+=4)>192) sprites[i].y=207;
     }
    }
   }
   else break;
  }
  i = 0;
  if (player1_health) {
      i = 3;
  }
  play_sound(5);
  play_sound(6);
  play_sound(7);
  play_sound(8);
  sprites[i].y=sprites[i+1].y=sprites[i+2].y=207;
  clear_sprites (6,10);
  center_string (18,"1 - Play again");
  center_string (19,"2 - Options   ");
  center_string (20,"3 - Intro     ");
  while ((keypad_1==0 || keypad_1>3) &&
         (keypad_2==0 || keypad_2>3));
  if (keypad_1==0 || keypad_1>3) i=keypad_2;
  else i=keypad_1;
  _fill_vram (chrtab+18*32,0,3*32);
  if (i==1) goto play_again;
  if (i==2) goto options;
  /* startup module will issue a soft reboot */
}
