/*	Copyright Alex Hornby 1994/1995. All rights reserved.
 	See file README for details
*/
extern "C"{
#include <stdio.h>
#include <stdlib.h>
#include <vga.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include "btypes.h"
#include "gamelib.h"
#include "screendefs.h"
}

#include <iostream.h>
#include "alopen.h"
#include "gif.h"
#include "asmblock.h"
#include "sprite.h"
#include "player.h"
#include "font.h"
#include "rnd.h"
#include "starfield.h"

#ifdef JOYSTICK
#include "Joystick.h"
#endif

#include "Keyboard.h"

#define NUMLASER 3
#define NUMBOMB 5
#define NUMALIEN 20

#define SHIPSLOT 0
#define LASERSLOT 10
#define ALIENSLOT 20
#define EXPLODESLOT 30 // and 4
#define BOMBSLOT 40
#define LIFESLOT 50
#define LOGOSLOT 60

Sprite bomb[NUMBOMB],laser[NUMLASER], alien[NUMALIEN], 
	logo, checkerboard, panel, life[NUMLIVES];
Player ship;

animList explode, shipStr, shipLeft, shipRight, shipExplode;
byte *vscreen;
StarField star;

#ifndef JOYSTICK
	Keyboard joy;
#else
	Joystick joy;
#endif

bool aliendead[NUMALIEN];
int ticks, aliens_left, level;
int sred[256], sgreen[256], sblue[256];

void
primetitle(void)
{
	void showmenu(void);
	showmenu();
	logo.setPos(115,0);
	logo.setVisible(TRUE);
}

int
main (void)
{
	void initialise(void);
	void getsprites(void);
	void display_background(void);
	void maingame( void);
	char choice;

	initialise();

	getsprites();
	cerr << "Got sprites" << endl;
	vga_setpalette(255, 30, 0, 0 ); 

	star.setGraphMem(vscreen);
	star.getBack();
	logo.getBack(115,0);

	/*
	int r,g,b;
	for(int x=0; x<256; x++)
	{
		vga_getpalette(x,&r, &g, &b);
		if(r==g && g==b && r!=0)
			cout << x << "," << r << endl;
	}
	*/
	
	primetitle();
	while(choice!='2')
	{
		switch(choice=vga_getkey())
		{
		case '1':
			logo.hideAll();
			maingame();
			primetitle();
			break;
		case '2':
			//print copyright
			break;
		default:
			break;
		}
		star.replace();
		logo.ReplaceAll();

		star.move();

		star.getBack();
		logo.getAll();

		star.paste();
		logo.PasteAll();

		screencopy(vscreen,graph_mem);	
	}
	/* Back to boring text mode! */
	for(int i=0;i<256;i++)
		vga_setpalette(i, sred[i], sgreen[i], sblue[i]); 
	vga_setmode(TEXT);
}

void showmenu(void)
{
	byte* v=vscreen;
	int mx=100,my=160;
	cls(v,0);
	printor("1:Start Game",v,mx,my);
	//printor("2:Instructions",v,mx,my+8);
	printor("2:Quit",v,mx,my+8);
}

void
initialise (void)
{
/* Set up screen mode */
	vga_init();
	vga_setmode(G320x200x256);

	for(int i=0;i<256;i++)
		vga_getpalette(i, &sred[i], &sgreen[i], &sblue[i]); 

/* Get font */
	fontinit();
}

void
getsprites (void)
{
	Gif gif;
	int i;

	/* Load picture containing sprites */
	FILE *fp=alopen("./invsprit.gif","/usr/local/lib/zapem/invsprit.gif","rb");	
	gif.Load(fp);
	gif.SetPalette();
	ship.setGraphMem(gif.GetPtr());		

	/* Cut the sprites */
	life[0].Cut(0,80,8,8,LIFESLOT);
	for(i=1;i<NUMLIVES;i++)
		life[i]=life[0];

	logo.Cut(0,88,92,24,LOGOSLOT);

	ship.Cut(0,0,16,16,SHIPSLOT);
	ship.Cut(16,0,16,16,SHIPSLOT+1);
	ship.Cut(32,0,16,16,SHIPSLOT+2);
	ship.Cut(48,0,16,16,SHIPSLOT+3);
	ship.Cut(64,0,16,16,SHIPSLOT+4);
	ship.Cut(80,0,16,16,SHIPSLOT+5);

	shipExplode=animElt(EXPLODESLOT,3)+
		animElt(EXPLODESLOT+1,3)+animElt(EXPLODESLOT+2,1)+animElt(SHIPSLOT,1);
	shipLeft=animElt(SHIPSLOT+2,5)+animElt(SHIPSLOT+3,5)+animElt(LOOPANIM,0);
	shipRight=animElt(SHIPSLOT+4,5)+animElt(SHIPSLOT+5,5)+animElt(LOOPANIM,0);
	shipStr=animElt(SHIPSLOT,5)+animElt(SHIPSLOT+1,5)+animElt(LOOPANIM,0);

	laser[0].Cut(0,16,8,16,LASERSLOT);
	for(i=1;i<NUMLASER;i++)
		laser[i]=laser[0];

	bomb[0].Cut(16,16,8,8,BOMBSLOT);
	for(i=1;i<NUMBOMB;i++)
		bomb[i]=bomb[0];

	alien[0].Cut(0,32,16,16,EXPLODESLOT);
	alien[0].Cut(16,32,16,16,EXPLODESLOT+1);	
	alien[0].Cut(32,32,16,16,EXPLODESLOT+2);	
	explode=animElt(EXPLODESLOT,2)+
		animElt(EXPLODESLOT+1,2)+animElt(EXPLODESLOT+2,1)+animElt(VANISHANIM,0);

	alien[0].Cut(0,48,16,16,ALIENSLOT);
	alien[0].Cut(16,48,16,16,ALIENSLOT+1);
	alien[0].Cut(32,48,16,16,ALIENSLOT+2);


	vscreen=new byte [65535];
	ship.setGraphMem(vscreen);	
}

void primesprites(void)
{
	ship.loadFilm(shipStr);
	ship.setDir(STRAIGHT);
	ship.setAnim(TRUE);

	animList aan=animElt(ALIENSLOT,5) + animElt(ALIENSLOT+1,5)+ animElt(ALIENSLOT+2,5) + animElt(LOOPANIM,0);  
	alien[0].loadFilm(aan);
	alien[0].setAnim(TRUE);

	for(int i=0;i<NUMALIEN;i++)
	{
		alien[i]=alien[0];
		aliendead[i]=FALSE;
	}


	ship.setPos(152,182);
	ship.getBack();

	for(i=0;i<ship.getLives();i++)
	{
		life[i].setPos(SCREENWIDTH-(i+1)*9,192);
		life[i].getBack();
	}

	i=0;
	for(int y=0; y<NUMALIEN/5; y++)
	{
		for(int x=0; x<NUMALIEN/4; x++)
		{
			alien[i].setPos(x*40+100,y*30+20);
			alien[i].getBack();
			alien[i].setDelta(-1,0);
			i++;
		}
	}
	ship.setVisible(TRUE);	
	for(i=0;i<ship.getLives();i++)
		life[i].setVisible(TRUE);
	for(i=0;i<NUMALIEN;i++)
		alien[i].setVisible(TRUE);
}

void display_background(void)
{
	for(int y=0; y<200; y+=16)
	{
		for(int x=0; x<320; x+=16)
			checkerboard.Paste(x,y);
	}
}


void 
fire(void)
{
static int last;
	if(ticks-last>40)
	{
	int i=0;
	while(laser[i].getVisible()==1 && i<NUMLASER)
		i++;
	if(i>=NUMLASER || laser[i].getVisible()==TRUE)
		return;
	laser[i].setDelta(0,-2);
	laser[i].getBack(0,0);
	laser[i].setPos(ship.getXp()+4, ship.getYp()-16);
	laser[i].setVisible(TRUE);
	last=ticks;
	}
}

int
control(int xp)
{
	/* Read joystick port */
	joy.fetch();
	
	if(ship.getDir()==UP && ship.getAnim()==TRUE)
		return xp;

	/* Act on the data read */
	if( joy.test(RIGHT) ) 
	{
		xp++;
		if(ship.getDir()!=RIGHT)
		{
			ship.setAnim(TRUE);
			ship.loadFilm(shipRight);
			ship.setDir(RIGHT);
		}
	}	
	else if( joy.test(LEFT) ) 
	{
		xp--; 
		if(ship.getDir()!=LEFT)
		{
			ship.setAnim(TRUE);
			ship.loadFilm(shipLeft);
			ship.setDir(LEFT);
		}
	}	
	else if (ship.getDir()!=STRAIGHT)
	{
		ship.setAnim(TRUE);
		ship.loadFilm(shipStr);
		ship.setDir(STRAIGHT);
	}

	if( joy.test(FIRE1) ) fire();

	/* Check for range */
	if(xp>304) xp=304;
	if(xp<0) xp=0;
	return xp;
}

void movelaser(void)
{
	for(int i=0;i<NUMLASER;i++)
	{
		laser[i].Move();
		if(laser[i].getYp()<0)
			{
			laser[i].setVisible(FALSE);
			laser[i].fixBack();
			}
	}
}

bool collide(void)
{
	for(int a=0; a<NUMALIEN; a++)
	{
		if( alien[a].getVisible()==TRUE &&
			ship.getYp() - alien[a].getYp() < 16 && 
			ship.getYp() - alien[a].getYp() >= 0 && 
			ship.getXp() - alien[a].getXp() < 16 && 
			ship.getXp() - alien[a].getXp() >= 0 )
		{
			return TRUE;
		}
	}		
	return FALSE;
}

void checklaser(void)
{
	for(int i=0;i<NUMLASER;i++)
	{
		if(laser[i].getVisible())
		{
			for(int a=0; a<NUMALIEN; a++)
			{
			if( !aliendead[a] &&
				laser[i].getYp() - alien[a].getYp() < 16 && 
				laser[i].getYp() - alien[a].getYp() >= 0 && 
				laser[i].getXp() - alien[a].getXp() < 16 && 
				laser[i].getXp() - alien[a].getXp() >= 0 )
				{
				aliens_left--;
				alien[a].loadFilm(explode);
				alien[a].setAnim(TRUE);
				aliendead[a]=TRUE;
				ship.addScore(10);
				laser[i].setVisible(FALSE);
				laser[i].fixBack();
				}
			}
		}
	}
}

void checkbomb(void)
{
	for(int i=0;i<NUMBOMB;i++)
	{
		if(bomb[i].getVisible())
		{
			if(
			bomb[i].getYp() - ship.getYp() < 16 && 
			bomb[i].getYp() - ship.getYp() >= 0 && 
			bomb[i].getXp() - ship.getXp() < 16 && 
			bomb[i].getXp() - ship.getXp() >= 0 )
			{
				ship.loadFilm(shipExplode);
				ship.setAnim(TRUE);
				ship.setDir(UP);
				life[ship.getLives()-1].setVisible(FALSE);
				life[ship.getLives()-1].fixBack();
				ship.subLife();	
				bomb[i].setVisible(FALSE);
				bomb[i].fixBack();	
			}			
		}
	}
}

void movebomb(void)
{
	for(int i=0;i<NUMBOMB;i++)
	{
		bomb[i].Move();
		if(bomb[i].getYp()> SCREENHEIGHT-bomb[i].getHeight() )
			{
			bomb[i].setVisible(FALSE);
			bomb[i].fixBack();
			}
	}
}

void 
alienfire( const Sprite& a)
{
	static int last;

	if(ticks-last>8)
	{
	int i=0;
	while(bomb[i].getVisible() && i<NUMBOMB)
		i++;
	if(bomb[i].getVisible() || i>=NUMBOMB)
		return;
	bomb[i].setDelta(0, 1+level/2);
	bomb[i].getBack(0,0);
	bomb[i].setPos(a.getXp()+4, a.getYp()+16);
	bomb[i].setVisible(TRUE);
	last=ticks;
	}
}

void movealiens(void)
{
	int left=0, right=0, curalien;
	
	/* Move the aliens */	
	for(curalien=0; curalien<NUMALIEN; curalien++)
	{
		if(alien[curalien].getVisible())
		{	
			if( alien[curalien].getXp() <= 0 )
				right=1;
			if( alien[curalien].getXp() > 303 )
				left=1;
		}
	}

	if(left)
	{
		for(curalien=0; curalien<NUMALIEN; curalien++)
			{
			alien[curalien].setDelta(-1,0);
			alien[curalien].Move(0,1+level);
			}
	}

	if(right)
	{
		for(curalien=0; curalien<NUMALIEN; curalien++)
		{
			alien[curalien].setDelta(1,0);
			alien[curalien].Move(0,1+level);
		}
	}

	for(curalien=0; curalien<NUMALIEN; curalien++)
	{
		alien[curalien].Move();
		if(!aliendead[curalien] && (alien[curalien].getXp()==ship.getXp() || rnd(100)>80) )
			alienfire(alien[curalien]);
	}
}


void levelintro(int l)
{
	char buffer[80];
	int x;
	cls(vscreen,0);
	sprintf(buffer," Level %d",l);
	for(x=0;x<SCREENWIDTH/2-32;x+=2)
	{
		print(buffer,vscreen,x,SCREENHEIGHT/2-4);
		waitvbl();
		screencopy(vscreen,graph_mem);
	}
	cls(vscreen,0);
}

void maingame(void)
{
	void gameinit(void);
	void game(void);

	level=1;
	gameinit();
	while(level<11 && ship.getAlive()==TRUE)
	{
		cls(vscreen,0);
		star.getBack();
		primesprites();
		levelintro(level);
		game();
		level++;
	}
	ship.hideAll();
	cls(vscreen,0);
}

void gameinit(void)
{
	ship.setScore(0);
	ship.setLives(3);
	ship.setAlive(TRUE);
	level=1;
}

void game(void)
{
	int key,xp,yp;
	xp=ship.getXp();yp=ship.getYp();
	char buffer[80];

	aliens_left=NUMALIEN;
	panel.Cut(0,0,80,8,80);

	while(key!='q')
	{
		if(joy.test(FIRE4)) ship.setAlive(FALSE);
		/* Move the player */
		xp=control(xp);
		ship.setPos( xp, yp);

		/* Move the aliens */
		if(ticks%2) movealiens();

		/*move the bullets*/	
		movelaser();
		movebomb();

		/*check bullets*/
		checklaser();
		checkbomb();

		/*Check for a win*/
		if(aliens_left<=0)
		{
			 key='q';
		}

		/*check if the player is dead*/

		if(collide() || ship.getAlive()==FALSE)
			return;

		/*************** Screen is updated here *********************/	
		/* First replace the background */
		ship.ReplaceAll();
		star.replace();

		star.move();

		/* Then get the new background */
		ship.getAll();
		star.getBack();

		/* Then paste on the graphics */
		panel.Paste(0,192);
		star.paste();
		ship.PasteAll();   

		sprintf(buffer,"%d",ship.getScore());
		printor(buffer, vscreen,0,192);

		//waitvbl()
		screencopy(vscreen,graph_mem);

		/*************** Count game ticks **************************/
		ticks++;
	}
}
