#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <dos.h>

#define NO_ERASE 0
#define ERASE 1

const int MAX_STARS = 70;

const int graphics_mode = 0x12;
const int maxscrnx = 640;
const int maxscrny = 480;
const int maxcolors = 16;

const float PERS_Z = 100;
const int z_generation_no = 1000;
const int z_factor = 5;
const int max_rand_z = z_generation_no;
const int max_rand_x = (long)(maxscrnx/2)*z_generation_no/PERS_Z;
const int max_rand_y = (long)(maxscrny/2)*z_generation_no/PERS_Z;;

const int start = 150;
const int incr = 250;
const unsigned long int d1 = (long)(start)*(start);
const unsigned long int d2 = (long)(start+1*incr)*(start+1*incr);
const unsigned long int d3 = (long)(start+2*incr)*(start+2*incr);
const unsigned long int d4 = (long)(start+3*incr)*(start+3*incr);
const unsigned long int d5 = (long)(start+4*incr)*(start+4*incr);

int z_decrement = 2;
const int start_speed = 20;	//milliseconds delay between successive frames
const int spd_incr = 3;
const int spd_max = 100;
const float blink_factor = 0.3;

typedef unsigned char byte;

struct {
	signed int x,y,z;
	byte color;
	} pixels[MAX_STARS];
byte prev_mode;
unsigned int speed = start_speed;

void set_mode(byte mode);
byte get_mode();
void set_pixel(int x, int y, byte color);
byte get_pixel(int x, int y);
void generate_initial_stars();
void display_stars(int erase=0);
void renew_out_of_view_stars();

void main()
	{
	int i;

	clrscr();

	printf("\n Press a key to move among the stars...!");
	printf("\n Up arrow key to increase speed, Down to decrease...");
	printf("\n Any other key to come back safely home...!");

	getch();
	prev_mode = get_mode();
	set_mode(graphics_mode);

	generate_initial_stars();

	while(1)
		{

		display_stars(NO_ERASE);

		delay(speed);

		display_stars(ERASE); //erase all stars

		delay(speed*blink_factor);

		//move towards the stars...
		for (i=0; i<MAX_STARS; i++)
			pixels[i].z -= z_decrement;

		renew_out_of_view_stars();

		if (kbhit())
			{
			char ch=getch();
			if (ch==0)
				{
				ch = getch();
				switch(ch)
					{
					case 'H': if (speed>=spd_incr) speed -= spd_incr; else z_decrement++; break;
					case 'P':	if (z_decrement>2) z_decrement--; else speed += spd_incr; break;
					default: goto out_of_loop;
					}
				}
			else
				break;
			}
		}

	out_of_loop:

	set_mode(prev_mode);

	printf("\n Please do come again..!");
	getch();
	}

byte get_mode()
	{
	byte to_return;
	asm {
	mov ah, 0FH
	int 10H
	mov to_return, al
	}
	return to_return;
	}


void set_mode(byte mode)
	{
	asm {
	mov ah, 00H
	mov al, mode
	int 10H
	}
	}

void set_pixel(int x, int y, byte color)
	{
	asm {
	mov ah, 0CH
	mov al, color
	mov bh, 0
	mov cx, x
	mov dx, y
	int 10H
	}
	}

byte get_pixel(int x, int y)
	{
	byte to_return;
	asm {
	mov ah, 0DH
	mov bh, 0
	mov cx, x
	mov dx, y
	int 10H
	mov to_return, al
	}
	return to_return;
	}

int map_to_2d(int x, int z)
	{
	return ((long)x * PERS_Z/z);
	}

void generate_initial_stars()
	{
	//generate stars
	randomize();
	for(int i=0; i<MAX_STARS; i++)
		{
		pixels[i].z = random(max_rand_z);
		float f = (pixels[i].z)/(2*PERS_Z);
		pixels[i].x = random(2*maxscrnx*f+1) - maxscrnx*f;
		pixels[i].y = random(2*maxscrny*f+1) - maxscrny*f;

		pixels[i].color = random(maxcolors);
		}
	}

void display_stars(int erase)
	{
	for(int i=0; i<MAX_STARS; i++)
			{
			int col = (erase==ERASE)? 0 : pixels[i].color;

			int x = map_to_2d(pixels[i].x, pixels[i].z)+maxscrnx/2;
			int y = map_to_2d(pixels[i].y, pixels[i].z)+maxscrny/2;

			if (x>0 && x<maxscrnx && y>0 && y<maxscrny)
				{
				unsigned long int dist = ((long)pixels[i].x*pixels[i].x) +
								((long)pixels[i].y*pixels[i].y) +
									((long)pixels[i].z*pixels[i].z*z_factor);

				set_pixel(x,y,col);

				if (dist<d4) set_pixel(x+1,y,col);
				if (dist<d3)	{ set_pixel(x,y+1,col);
									set_pixel(x+1,y+1,col); }
				if (dist<d2) { set_pixel(x-1,y,col);
										set_pixel(x-1,y+1,col); }
				if (dist<d1) { set_pixel(x,y-1,col);
									set_pixel(x+1,y-1,col);
										set_pixel(x-1,y-1,col); }
				}
			}
	}


void renew_out_of_view_stars()
	{
	//replace out of view stars with new stars
	for (int i=0; i<MAX_STARS; i++)
		{
		if ( pixels[i].z <=0 ||
			abs(map_to_2d(pixels[i].x, pixels[i].z)) > (maxscrnx/2) ||
			abs(map_to_2d(pixels[i].y, pixels[i].z)) > (maxscrny/2) )
			{	//out of view star - replace it with new star at the center
			pixels[i].x = random(2*max_rand_x+1) - max_rand_x;
			pixels[i].y = random(2*max_rand_y+1) - max_rand_y;
			pixels[i].z = z_generation_no;//random(max_rand_z);
			pixels[i].color = random(maxcolors);
			}
		}
	}
