//********************************************************
// Homework 1.a 
// Scott Fadick
// CSIS 240
//
// Desc: This is a modified program from the textbook
//       "Windows Game Programing for DUMMIES". It was
//       PROG2_1.CPP - A simple console based game to illustrate
//       a generic game loop
//
// INCLUDES ///////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h> 
#include <conio.h>
#include <windows.h>
#include <time.h>

// DEFINES ////////////////////////////////////////////////
#define MAX_X        79  // maximum x position for player
#define SCROLL_POS   24  // the point that scrolling occurs
#define MAX_Y        24

#define GAME_STATE_INIT   1
#define GAME_STATE_INTRO  2
#define GAME_STATE_GAME   3
#define GAME_STATE_WINNER 4

#define OB_STATE_GOOD   1
#define OB_STATE_HIT    2
#define OB_STATE_HIT1   3
#define OB_STATE_HIT2   4
#define OB_STATE_HIT3   5
#define OB_STATE_SHOT   6
#define OB_STATE_OFF    7

// TYPEDEFS////////////////////////////////////////////////
typedef struct GAME_OB_TYP
{
	int state;// condition of thing
	int timer;
	int count;// used by state to update object
	int x,prev_x;// x position
	int y,prev_y;// y position
	int dx;// change in x
	int dy;// change in y
	int color;
	char graphic[5];// holds chars to print
	char erase[5];
} PLAYER, *PPLAYER;

// PROTOTYPES /////////////////////////////////////////////
void Init_Graphics(void);
inline void Set_Color(int fcolor, int bcolor);
inline void Draw_String(int x,int y, char *string);
void Check_Key_Press(void);
void Reset_Thing(PPLAYER pplayer);
void Draw_Thing(PPLAYER pplayer);
void Game_Loop(void);
void Update_Thing(PPLAYER pplayer);
void Set_Up_Game(void);
void Update_Shot(void);
void Draw_Stars(void);
void DO_AI(void);

// GLOBALS ////////////////////////////////////////////////
CONSOLE_SCREEN_BUFFER_INFO    con_info;// holds screen info
HANDLE                        hconsole=NULL;// handle to console
int							  num_players=0;
int                           game_running = 1;// state of game, 0=done, 1=run
int							  game_state=GAME_STATE_INIT;
int                           count=0;//number of frames
int							  speed=2;
PLAYER						  player_1,player_2,pshot_1,pshot_2;

// FUNCTIONS //////////////////////////////////////////////
void Do_AI(void)
{
	//AI
	if(count%15)
	{
		int move=rand()%6;
		// is player two thrust left   	
		if (move==1)
		{
			player_2.dx--;
			if(player_2.dx<-1)player_2.dx=-1;
		}
		// is player two thrust right
		if (move==2)
		{
		    player_2.dx++;
		    if(player_2.dx>1)player_2.dx=1;
		}
		//player two thrust up
		if (move==3)
		{
		    player_2.dy--;
			if(player_2.dy<-1)player_2.dy=-1;
		}
		// is player two thrust down
		if (move==4)
		{
			player_2.dy++;
			if(player_2.dy>1)player_2.dy=1;
		}
		//player two shoots
		if(move==0 || move==5 || move==6)
		{
			if(pshot_2.state==OB_STATE_OFF)
			{
				pshot_2.state=OB_STATE_SHOT;
				pshot_2.count=30;
				pshot_2.x=player_2.x+1;
				pshot_2.y=player_2.y;
				pshot_2.dx=pshot_2.dy=0;
				//
				if(player_1.x>player_2.x+4)pshot_2.dx=1;
				if(player_1.x<player_2.x-1)pshot_2.dx=-1;
				if(player_1.y>player_2.y+1)pshot_2.dy=1;
				if(player_1.y<player_2.y-1)pshot_2.dy=-1;
			}
		}
	}
}
///////////////////////////////////////////////////////////
void Update_Shot(void)
{
	//update shot
	if(pshot_1.state==OB_STATE_SHOT)
	{
		if(pshot_1.y==player_2.y)
			if(pshot_1.x==player_2.x ||
				pshot_1.x==player_2.x+1 ||
				pshot_1.x==player_2.x+2 ||
				pshot_1.x==player_2.x+3)
			{
				pshot_1.state=OB_STATE_OFF;
				player_2.state=OB_STATE_HIT;
			}
	}
	if(pshot_2.state==OB_STATE_SHOT)
	{
		if(pshot_2.y==player_1.y)
			if(pshot_2.x==player_1.x ||
				pshot_2.x==player_1.x+1 ||
				pshot_2.x==player_1.x+2 ||
				pshot_2.x==player_1.x+3)
			{
				pshot_2.state=OB_STATE_OFF;
				player_1.state=OB_STATE_HIT;
			}
	}
}
///////////////////////////////////////////////////////////
void Set_Up_Game(void)
{
	//Set Up
	player_1.state=OB_STATE_GOOD;
	player_1.count=5;//number of lives
	player_1.color=5;
	player_1.x=60;
	player_1.y=12;
	player_2.state=OB_STATE_GOOD;
	player_2.count=5;
	player_2.color=2;
	player_2.x=20;
	player_2.y=12;
	//
	sprintf(player_1.graphic,"<\\/>");
	sprintf(player_2.graphic,"/()\\");
	sprintf(pshot_1.graphic,"*");
	sprintf(pshot_2.graphic,"*");
	sprintf(pshot_1.erase," ");
	sprintf(pshot_2.erase," ");
}
///////////////////////////////////////////////////////////
void Update_Thing(PPLAYER pplayer)
{
	int flag=0;
	//update
	pplayer->prev_x=pplayer->x;
	pplayer->prev_y=pplayer->y;
	pplayer->x+=pplayer->dx;
	pplayer->y+=pplayer->dy;
	if(pplayer->x>MAX_X-7 || pplayer->x<3)
	{
		pplayer->dx*=-1;
		pplayer->x+=pplayer->dx;
		flag++;
	}
	if(pplayer->y>MAX_Y-1 || pplayer->y<1)
	{
		pplayer->dy*=-1;
		pplayer->y+=pplayer->dy;
		flag++;
	}
	switch(pplayer->state)
	{
	case OB_STATE_SHOT:
		{
			pplayer->color=(pplayer->color+1)%15;
			if(pplayer->count--<1 || flag)pplayer->state=OB_STATE_OFF;
			Update_Shot();
			break;
		}
	case OB_STATE_HIT:
		{
			pplayer->dx=0;
			pplayer->dy=0;
			pplayer->timer=30;
			pplayer->state=OB_STATE_HIT1;
			break;
		}
	case OB_STATE_HIT1:
		{
			pshot_1.state=OB_STATE_OFF;
			pshot_2.state=OB_STATE_OFF;
			sprintf(pplayer->graphic,"*XX*");
			pplayer->color=rand()%15;
			if(pplayer->timer--<1)pplayer->state=OB_STATE_HIT2;
			break;
		}
	case OB_STATE_HIT2:
		{
			if(--pplayer->count<1)game_state=GAME_STATE_WINNER;
			pplayer->state=OB_STATE_GOOD;
			sprintf(player_1.graphic,"<\\/>");
			sprintf(player_2.graphic,"/()\\");
			player_1.color=5;
			player_2.color=2;
			break;
		}
	}
}/////////////////////////////////////////////////////////
void Game_Loop(void)
{
	//Game Loop
	if(_kbhit())Check_Key_Press();
	if(num_players==1)Do_AI();
	//
	Update_Thing(&player_1);
	Update_Thing(&player_2);
	Update_Thing(&pshot_1);
	Update_Thing(&pshot_2);
	//
	Draw_Thing(&pshot_1);
	Draw_Thing(&pshot_2);
	Update_Thing(&pshot_1);
	Update_Thing(&pshot_2);
	//
	Draw_Thing(&player_1);
	Draw_Thing(&player_2);
	Draw_Thing(&pshot_1);
	Draw_Thing(&pshot_2);
	
}
///////////////////////////////////////////////////////////
void Draw_Thing(PPLAYER pplayer)
{
	//Draw
	Set_Color(0,0);
	Draw_String(pplayer->prev_x,pplayer->prev_y,pplayer->erase);
	Set_Color(pplayer->color,0);
	if(pplayer->state!=OB_STATE_OFF)
		Draw_String(pplayer->x,pplayer->y,pplayer->graphic);
}
//////////////////////////////////////////////////////////
void Reset_Thing(PPLAYER pplayer)
{
	char str[5]="    ";
	//reset
	pplayer->state=OB_STATE_OFF;// condition of thing
	pplayer->count=0;// used by state to update object
	pplayer->x=pplayer->prev_x=MAX_X/2;// x position
	pplayer->y=pplayer->prev_y=MAX_Y/2;// y position
	pplayer->dx=0;// change in x
	pplayer->dy=0;// change in y
	pplayer->color=0;
	sprintf(pplayer->graphic,str);
	sprintf(pplayer->erase,str);
}
///////////////////////////////////////////////////////////
void Check_Key_Press(void)
{
	// input keys
	char key;
	// get keyboard data, and filter it
	key = toupper(getch());
	// is player trying to exit, if so exit
	if (key=='Q' || key==27)
		game_running = 0; 
	// is player one thrust left   	
	if (key=='K')
	{
		player_1.dx--;
		if(player_1.dx<-1)player_1.dx=-1;
	}
	// is player one thrust right
    if (key=='M')
	{
        player_1.dx++;
		if(player_1.dx>1)player_1.dx=1;
	}
	//player one thrust up
	if (key=='H')
	{
        player_1.dy--;
		if(player_1.dy<-1)player_1.dy=-1;
	}
    // is player one thrust down
    if (key=='P')	
	{
        player_1.dy++;
		if(player_1.dy>1)player_1.dy=1;
	}
	//player one shoots
	if(key=='/')
	{
		if(pshot_1.state==OB_STATE_OFF)
		{
			pshot_1.state=OB_STATE_SHOT;
			pshot_1.count=30;
			pshot_1.x=player_1.x+1;
			pshot_1.y=player_1.y;
			pshot_1.dx=pshot_1.dy=0;
			//
			if(player_2.x>player_1.x+4)pshot_1.dx=1;
			if(player_2.x<player_1.x-1)pshot_1.dx=-1;
			if(player_2.y>player_1.y+1)pshot_1.dy=1;
			if(player_2.y<player_1.y-1)pshot_1.dy=-1;
		}
	}
	if(num_players>1)//check for valid player two input
	{
		// is player two thrust left   	
		if (key=='D')
		{
			player_2.dx--;
			if(player_2.dx<-1)player_2.dx=-1;
		}
		// is player two thrust right
		if (key=='F')
		{
	       player_2.dx++;
		   if(player_2.dx>1)player_2.dx=1;
		}
		//player two thrust up
		if (key=='R')
		{
		    player_2.dy--;
			if(player_2.dy<-1)player_2.dy=-1;
		}
	    // is player two thrust down
		if (key=='C')
		{
			player_2.dy++;
			if(player_2.dy>1)player_2.dy=1;
		}
		//player two shoots
		if(key=='A')
		{
			if(pshot_2.state==OB_STATE_OFF)
			{
				pshot_2.state=OB_STATE_SHOT;
				pshot_2.count=30;
				pshot_2.x=player_2.x+1;
				pshot_2.y=player_2.y;
				pshot_2.dx=pshot_2.dy=0;
				//
				if(player_1.x>player_2.x+4)pshot_2.dx=1;
				if(player_1.x<player_2.x-1)pshot_2.dx=-1;
				if(player_1.y>player_2.y+1)pshot_2.dy=1;
				if(player_1.y<player_2.y-1)pshot_2.dy=-1;
			}
		}
	}
}
///////////////////////////////////////////////////////////
void Init_Graphics(void)
{
	// this function initializes the console graphics engine
	COORD console_size = {80,25}; // size of console

	// seed the random number generator with time
	srand((unsigned)time(NULL));

	// open i/o channel to console screen
	hconsole=CreateFile("CONOUT$",GENERIC_WRITE | GENERIC_READ,
		     FILE_SHARE_READ | FILE_SHARE_WRITE,
			 0L, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0L);

	// make sure we are in 80x25
	SetConsoleScreenBufferSize(hconsole,console_size);

	// get details for console screen                       
	GetConsoleScreenBufferInfo(hconsole,&con_info);
} // end Init_Graphics
///////////////////////////////////////////////////////////

inline void Set_Color(int fcolor, int bcolor=0) 
{
	// this function sets the color of the console output
	SetConsoleTextAttribute(hconsole,(WORD)((bcolor << 4) | 
		                    fcolor));
} // Set_Color

///////////////////////////////////////////////////////////

inline void Draw_String(int x,int y, char *string)
{
	// this function draws a string at the given x,y
	COORD cursor_pos; // used to pass coords

	if(x<0 || x>MAX_X-(int)strlen(string) || y<0 || y>MAX_Y)
		return;//check for out of bounds

	cursor_pos.X = x;// set printing position
	cursor_pos.Y = y;
	SetConsoleCursorPosition(hconsole,cursor_pos);

	printf("%s",string);// print the string in current color
} // end Draw_String
///////////////////////////////////////////////////////////

inline void Clear_Screen(void)
{
	// this function clears the screen
	Set_Color(15,0);// set color to white on black

	for (int index=0; index<=25; index++)// clear the screen
    Draw_String(0, SCROLL_POS,"\n");
} // end Clear_Screen
// MAIN GAME LOOP /////////////////////////////////////////

void main(void)
{
	// set up the console text graphics system
	char key;
	char str[255];//array for outputting score
	Init_Graphics();
	Clear_Screen();
	while(game_running)
	{
		count++;
		switch(game_state)
		{
		case GAME_STATE_INIT:
			{		
				Set_Color(2,0);			
				Draw_String(10,10,"Player two keys:");
				Draw_String(10,13,"R D F C and A to FIRE!");
				Set_Color(5,0);
				Draw_String(40,10,"Player one keys:");
				Draw_String(40,13,"Arrow Keys and '/' to FIRE!");
				Set_Color(count%15,0);
				Draw_String(23,3,"Scott Fadick, CSIS 240, Presents:");
				Draw_String(24,16,"Press '+' / '-' to change speed.");
				sprintf(str,"Speed: %i  ",10-speed);
				Draw_String(35,18,str);
				Draw_String(24,20,"Press '1' to start one player!!!");
				Draw_String(24,22,"Press '2' to start two player!!!");
				Set_Color((count+8)%15,0);
				Draw_String(27,5,"!!! CHAR WARS in SPACE !!!");
				if(_kbhit())
				{
					// get keyboard data, and filter it
					key = toupper(getch());
					// is player trying to exit, if so exit
					if (key=='1')
					{
						num_players=1;
						game_state=GAME_STATE_INTRO;
					}
					if (key=='2')
					{
						num_players=2;
						game_state=GAME_STATE_INTRO;
					}
					if(key=='-')
						if(++speed>10)speed=10;
					if(key=='=')
						if(--speed<0)speed=0;
				}
				break;
			}
		case GAME_STATE_INTRO:
			{
				count=0;
				Clear_Screen();
				Reset_Thing(&player_1);
				Reset_Thing(&player_2);
				Reset_Thing(&pshot_1);
				Reset_Thing(&pshot_2);
				Set_Up_Game();
				game_state=GAME_STATE_GAME;
				break;
			}
		case GAME_STATE_GAME:
			{
				Game_Loop();
				sprintf(str, "Player 1: %i Lives   Player 2: %i Lives"
					,player_1.count
					,player_2.count);
				Set_Color(7,0);
		        Draw_String(0,0,str);//set cursor to upper left
		        // SECTION: synchronize to a constant frame rate
		        Sleep(10+speed*12);
				break;
			}
		case GAME_STATE_WINNER:
			{
				Set_Color(count%15,0);
				if(player_1.count>0)
					Draw_String(30,10,"Player one wins!!!!");
				if(player_2.count>0 && num_players==2)
					Draw_String(30,10,"Player two wins!!!!");
				if(player_2.count>0 && num_players==1)
					Draw_String(30,10,"The Computer wins!!!!");
				Draw_String(25,20,"Press [C]ontinue or [Q]uit.");
				if(_kbhit())
				{
					// get keyboard data, and filter it
					key = toupper(getch());
					// is player trying to exit, if so exit
					if (key=='Q' || key==27 || key=='N')
						game_running = 0; 
					// player continue
					if(key=='C')
					{
						game_state=GAME_STATE_INIT;
						Clear_Screen();
					}
				}
			}
		}
	}// end while
	// SECTION: shutdown and bail
    Clear_Screen();
    printf("\nG A M E  O V E R \n\n");
} // end main
//eof****************************