/*
------------------------------------------------------------------

Black Nebula

File :				3drout.c
Programmer:		Colin Adams
Date:				4/3/91
Last Modified :	10/6/91

------------------------------------------------------------------

*/

#define D3ROUT
#define AMIGA_INCLUDES

#include "3d.h"

extern object myship;
extern short swapflag, U_rot;
extern short num_active[];

extern struct BitMap my_bit_map;
extern struct BitMap back_bit_map;

short store1 = 0, store2 = 0;

/*	------------------------------------------------------------------
		Fast Sin/Cos Routines
		------------------------------------------------------------------
*/

double deg(int degrees)
/* converts degrees to radians */
{
	double test = degrees;
	double one8 = 180;
	
	return 3.141592654/(one8/test);
}

void TrigSetUp(void)
{
	register int i;
	FILE *fp;
	
	if(!(fp=fopen("trig.pre","r")))
	{
		printf("Creating trig tables...\n");
	
		for(i=0; i<=90; i++)
		{
			sintable[i] = ((double) sin(deg(i)) * 65536);
			costable[i] = ((double) cos(deg(i)) * 65536);
		}
	
		/* precalculate sin/cos for all quadrants */
		
		for(i=91; i<=360; i++)
		{
			short six = 0, cix = 0, rot = i;
		
			if(rot>=271)
			{
				rot = 360 - rot;
				six = 1;
			}
			else if(rot>=181)
			{
				rot -= 180;
				six = cix = 1;
			}
			else if(rot>=91)
			{
				rot = 180 - rot;
				cix = 1;
			}
		
			cix = cix ? -1 : 1;
			six = six ? -1 : 1;
		
			sintable[i] = ((double) sin(deg(rot)) * 65536)*six;
			costable[i] = ((double) cos(deg(rot)) * 65536)*cix;
		}
	
		printf("Saving...\n");
	
		if(!(fp = fopen("trig.pre","w")))
		{
			printf("Failed to save trig data!!!\n");
			return;
		}
	
		fwrite((void *) &sintable[0], sizeof(int), 361, fp);
		fwrite((void *) &costable[0], sizeof(int), 361, fp);
		fclose(fp);
	}
	else
	{
		fread((void *) &sintable[0], sizeof(int), 361, fp);
		fread((void *) &costable[0], sizeof(int), 361, fp);
		fclose(fp);
	}
}

/*	------------------------------------------------------------------
		Random Number Functions
		------------------------------------------------------------------
*/

void SetRandom(void)
{
	long t;
	time(&t);
	srand((int) t);
}

int getrandom(int a, int b)
{
	return (rand() % (1+b-a)) + a;
}
	
/*	------------------------------------------------------------------
		Routines for Object Manipulation
		------------------------------------------------------------------
*/


void Rotate_Obj_Abs(object *obj, short x, short y, short z)
{
	obj->rot_x = x;
	obj->rot_y = y;
	obj->rot_z = z;
}

void Rotate_Obj_Rel(object *obj, short x, short y, short z)
{
	if((obj->rot_x += x)>=360)
		obj->rot_x -= 360;
	else if(obj->rot_x<0)
		obj->rot_x += 360;
		
	if((obj->rot_y += y)>=360)
		obj->rot_y -= 360;
	else if(obj->rot_y<0)
		obj->rot_y += 360;
	
	if((obj->rot_z += z)>=360)
		obj->rot_z -= 360;
	else if(obj->rot_z<0)
		obj->rot_z += 360;
}

void Translate_Obj_Abs(object *obj, short x, short y, short z)
{
	obj->trans_x = x;
	obj->trans_y = y;
	obj->trans_z = z;
}	

void Translate_Obj_Rel(object *obj, short x, short y, short z)
{
	obj->trans_x += x;
	obj->trans_y += y;
	obj->trans_z += z;
}

void Destroy_Object(object *obj)
/* pretty obvious what this does */
{
	polygon *p, *nextp;
	
	p = obj->poly;
	while(p)
	{
		nextp = (polygon *) p->next;
		free(p);
		p = nextp;
	}
	
	free(obj);
}

void Save_Object(object *obj, char *filename)
{
	FILE *fp;
	polygon *p;
	int zero = 0, one = 1;
	
	if(!(fp=fopen(filename,"w")))
	{
			printf("Disk Error : Couldn't open the file for output!\n");
			return;
	}

	fwrite((void *) &(obj->type), sizeof(char), 1, fp);
	fwrite((void *) &(obj->start_x), sizeof(short), 1, fp);
	fwrite((void *) &(obj->start_y), sizeof(short), 1, fp);
	fwrite((void *) &(obj->start_z), sizeof(short), 1, fp);
	fwrite((void *) &(obj->numpoints), sizeof(short), 1, fp);
	fwrite((void *) &(obj->radius), sizeof(short), 1, fp);
	fwrite((void *) &(obj->objpoints[0]), sizeof(point), MAX_OBJ_POINTS, fp);

	p = obj->poly;
	
	while(p)
	{
		fwrite((void *) &one, sizeof(int), 1, fp);
		
		fwrite((void *) &(p->numpoints), sizeof(char), 1, fp);
		fwrite((void *) &(p->colour), sizeof(short), 1, fp);
		fwrite((void *) &(p->centre), sizeof(point), 1, fp);		
		fwrite((void *) &(p->p[0]), sizeof(short), MAX_POINTS, fp);

		p = (polygon *) p->next;
	}
	
	fwrite((void *) &zero, sizeof(int), 1, fp);
	fclose(fp);
	printf("Object saved to file.\n");
}

int Load_Object(object *obj, char *filename)
{
	FILE *fp;
	polygon *p;
	int i;
	
	if(!(fp=fopen(filename,"r")))
	{
		printf("Disk Error : Couldn't open the file for input!\n");
		return 0;
	}

	fread((void *) &(obj->type), sizeof(char), 1, fp);
	fread((void *) &(obj->start_x), sizeof(short), 1, fp);
	fread((void *) &(obj->start_y), sizeof(short), 1, fp);
	fread((void *) &(obj->start_z), sizeof(short), 1, fp);
	fread((void *) &(obj->numpoints), sizeof(short), 1, fp);
	fread((void *) &(obj->radius), sizeof(short), 1, fp);
	
	fread((void *) &(obj->objpoints[0]), sizeof(point), MAX_OBJ_POINTS, fp);
	
	fread((void *) &i, sizeof(int), 1, fp);
	
	if(i)
	{
		obj->poly = (polygon *) malloc(sizeof(polygon));
		p = obj->poly;
		
		fread((void *) &(p->numpoints), sizeof(char), 1, fp);
		fread((void *) &(p->colour), sizeof(short), 1, fp);
		fread((void *) &(p->centre), sizeof(point), 1, fp);		
		fread((void *) &(p->p[0]), sizeof(short), MAX_POINTS, fp);

		fread((void *) &i, sizeof(int), 1, fp);
		while(i)
		{
			p->last_num = p->clip_num = p->last_num2 = 0;
			p->next = (polygon *) malloc(sizeof(polygon));
			p = (polygon *) p->next;
			
			fread((void *) &(p->numpoints), sizeof(char), 1, fp);
			fread((void *) &(p->colour), sizeof(short), 1, fp);
			fread((void *) &(p->centre), sizeof(point), 1, fp);		
			fread((void *) &(p->p[0]), sizeof(short), MAX_POINTS, fp);

			fread((void *) &i, sizeof(int), 1, fp);
		}
		p->next = NULL;
	}
		
	fclose(fp);
	return 1;
}

object *CreateObject(void)
{
	object *obj;
	
	if(!(obj = (object *) malloc(sizeof(object))))
	{
		printf("Fatal Error : Not enough memory!\n");
		CleanUpandExit();
	}
	
	obj->poly = NULL;
	obj->start_x = obj->start_y = obj->start_z = 0;
	obj->type = obj->centre_x = obj->centre_y = obj->centre_z = 0;
	obj->trans_x = obj->trans_y = obj->trans_z = 500;
	obj->rot_x = obj->rot_y = obj->rot_z = 0;
	obj->velocity = 5;
	obj->heading = 0;
	return obj;
}	

void AddPoly(object *obj, polygon *p)
{
	polygon *point = obj->poly;
	if(point)
	{
		while(point->next)
			point = (polygon *) point->next;
		
		point->next = p;
		p->next = NULL;
	}
	else
	{
		obj->poly = p;
		p->next = NULL;
	}
}

void SpinThatObject(object *obj)
/*
Spins an object around the axies.  Seems to be a bottleneck, so will
have to be converted to 68k.  Anyone with a faster algorithm please tell
me, as this is the fastest one I know.
*/
{
	register short i;
	register short xrot = obj->rot_x;
	register short yrot = obj->rot_y;
	register short zrot = obj->rot_z;
	
	for(i=1; i<=obj->numpoints; i++)
	{
		register int x,y,z;
		
		x = obj->objpoints[i].x;
		y = obj->objpoints[i].y;
		z = obj->objpoints[i].z;

		/* rotate a point around the z axis */
		
		if(zrot)
		{
			register int temp, temp2, temp3;
			temp = x;
			temp2 = costable[zrot];
			temp3 = sintable[zrot];
			
			x = ((temp2*temp)>>16) - ((temp3*y)>>16);
			y = ((temp3*temp)>>16) + ((temp2*y)>>16);
		}
			
		/* rotate a point around the x axis */
			
		if(xrot)
		{
			register int temp, temp2, temp3;
			temp = y;
			temp2 = costable[xrot];
			temp3 = sintable[xrot];
			
			y = ((temp2*temp)>>16) - ((temp3*z)>>16);
			z = ((temp3*temp)>>16) + ((temp2*z)>>16);
		}
			
		/* rotate a point around the y axis */
			
		if(yrot)
		{
			register int temp, temp2, temp3;
			temp = z;
			temp2 = costable[yrot];
			temp3 = sintable[yrot];
			
			z = ((temp2*temp)>>16) - ((temp3*x)>>16);
			x = ((temp3*temp)>>16) + ((temp2*x)>>16);
		}
			
		/* move back to points */
		
		manipulate_x[i] = x;
		manipulate_y[i] = y;
		manipulate_z[i] = z;
	}	
}

void SpinObjCentres(void)
/* another use of my rotate code */
{
	register short i;
	
	for(i=0; i<no_objects; i++)
	{
		register object *obj = active_list[i];
		register int x,y,z;
	
		register short xrot = obj->rot_x;
		register short yrot = obj->rot_y;
		register short zrot = obj->rot_z;

		x = obj->start_x;
		y = obj->start_y;
		z = obj->start_z;

		/* rotate a point around the z axis */
		
		if(zrot)
		{
			register int temp, temp2, temp3;
			temp = x;
			temp2 = costable[zrot];
			temp3 = sintable[zrot];
			
			x = ((temp2*temp)>>16) - ((temp3*y)>>16);
			y = ((temp3*temp)>>16) + ((temp2*y)>>16);
		}
			
		/* rotate a point around the x axis */
			
		if(xrot)
		{
			register int temp, temp2, temp3;
			temp = y;
			temp2 = costable[xrot];
			temp3 = sintable[xrot];
			
			y = ((temp2*temp)>>16) - ((temp3*z)>>16);
			z = ((temp3*temp)>>16) + ((temp2*z)>>16);
		}
			
		/* rotate a point around the y axis */
			
		if(yrot)
		{
			register int temp, temp2, temp3;
			temp = z;
			temp2 = costable[yrot];
			temp3 = sintable[yrot];
			
			z = ((temp2*temp)>>16) - ((temp3*x)>>16);
			x = ((temp3*temp)>>16) + ((temp2*x)>>16);
		}
			
		/* move back to points */
		
		obj->centre_x = x;
		obj->centre_y = y;
		obj->centre_z = z;
	}	
}

void PrepareObject(object *obj)
{
	polygon *pg = obj->poly;
	register short i;

	if(!(obj->drawme))
		return;

	SpinThatObject(obj);

	for(i=1; i<=obj->numpoints; i++) /* do translation */
	{
		manipulate_x[i] += obj->trans_x;
		manipulate_y[i] += obj->trans_y;
		manipulate_z[i] += obj->trans_z;
	}
	
	DepthSort(obj); /* sort polygons by depth from eye */
	
	/* convert all points in object to 2d */
	
	for(i=1; i<=obj->numpoints; i++)
	{
		get2d(manipulate_x[i], manipulate_y[i], manipulate_z[i]);
		obj->pointx[i] = x;
		obj->pointy[i] = y;
	}

	/* work out what points are in each polygon, then clip them */
	
	while(pg)
	{
		for(i=0; i<pg->numpoints; i++)
		{
			pg->x[i] = obj->pointx[pg->p[i]];
			pg->y[i] = obj->pointy[pg->p[i]];
		}
		if(!pg->back_face)
		 	polygon_clip(pg); /* creates a new polygon clipped */
		pg = (polygon *) pg->next;
	}
}

void DrawObject(object *obj)
/* can only be used on an object that has already been clipped and
arranged in drawing order */
{
	register int j = 0;
	register polygon *pg;

	pg = obj->draworder[j];

	obj->lastdraw2 = obj->lastdrawme;
	obj->lastdrawme = obj->drawme;

	if(!(obj->drawme))
		return;
		
	while(pg) /* list is only terminated with a NULL */
	{
		register int i;
		
		if(!pg->back_face)
		{
			SetAPen(rastport, pg->colour);
			
			MoveTo(pg->clip_x[0], pg->clip_y[0]);
	
			for(i=1; i<pg->clip_num; i++)
				DrawTo(pg->clip_x[i], pg->clip_y[i]);
				
			FillArea();
		}

		if(swapflag)
		{
			store1 = 1;
			
			pg->last_num = pg->clip_num;
		
			for(i=0; i<pg->clip_num; i++)
			{
				pg->last_x[i] = pg->clip_x[i];
				pg->last_y[i] = pg->clip_y[i];
			}
		}
		else
		{
			store2 = 1;
			
			pg->last_num2 = pg->clip_num;
		
			for(i=0; i<pg->clip_num; i++)
			{
				pg->last_x2[i] = pg->clip_x[i];
				pg->last_y2[i] = pg->clip_y[i];
			}
		}
		
		j++;
		pg = obj->draworder[j];
	}
}

void EraseObject(object *obj)
/* can only be used on an object that has already been drawn */
{
	polygon *pg;

	if(!(obj->lastdraw2))
		return;

	pg = obj->poly;
	SetAPen(rastport, 0);
	
	if(swapflag)
	{
		if(store1)
		{
			while(pg)
			{
				register int i;

				if(pg->last_num)
				{
					MoveTo(pg->last_x[0], pg->last_y[0]);
				
					for(i=1; i<pg->last_num; i++)
						DrawTo(pg->last_x[i], pg->last_y[i]);
					
					FillArea();
				}
				pg = (polygon *) pg->next;
			}
		}
	}
	else
	{
		if(store2)
		{
			while(pg)
			{
				register int i;
				
				if(pg->last_num2)
				{
					MoveTo(pg->last_x2[0], pg->last_y2[0]);

					for(i=1; i<pg->last_num2; i++)
						DrawTo(pg->last_x2[i], pg->last_y2[i]);
				
					FillArea();

				}
				pg = (polygon *) pg->next;
			}
		}
	}
}

void AddObject(char *filename, short type)
{
	object *obj;
	int i;
	
	for(i=0; i<MAX_OBJECTS; i++)
	{	
		obj = CreateObject();
		if(!(Load_Object(obj, filename)))
		{
			printf("Failed to load %s\n",filename);
			free(obj);
			return;
		}
		memcpy(&obj_types[type][i], obj, sizeof(object));	
		free(obj);
	}
}

void SetUpExplosions(void)
{
	int i;
	
	for(i=0; i<MAX_EXPLOSIONS; i++)
	{
		explosions[i].type = EXPLOSION;
		explosions[i].radius = 2500;

		if(!(explosions[i].poly = malloc(sizeof(polygon))))
		{
			printf("Error creating explosions!\n");
			CleanUpandExit();
		}
		explosions[i].poly->next = NULL;
		explosions[i].poly->last_num = explosions[i].poly->last_num2 = 0;
	}
}

void AddNewObject(short type, short x, short y, short z)
{
	register int i;
	register object *obj;
		
	for(i=0; i<MAX_OBJECTS; i++)
	{
		if(!obj_free[type][i])
		{
			obj = &obj_types[type][i];
			
			Translate_Obj_Abs(obj, x, y, z);
			
			obj->i_am_dying = obj->lastdrawme = obj->lastdraw2 = 0;
			obj->timeinflight = 0;
			
			if(type==MYMISSILE || type==GUIDEDMISSILE)
			{
				
				obj->velocity = 30;
				obj->heading = View_Angle;
				obj->target = NULL;
				if(type==MYMISSILE)
					Rotate_Obj_Abs(obj, 0, View_Angle, 180);
				else
					Rotate_Obj_Abs(obj, 0, View_Angle, 0);
			}
			
			if(type==PLAYER || type==MAMBA)
				Rotate_Obj_Abs(obj, 90, 0, 0);
			
			obj->explode = 0;
			obj_free[type][i] = 1;
			active_list[no_objects] = obj;
			no_objects++;
			num_active[type]++;
			return;
		}
	}
}

void KillAllObjects(void)
{
	unsigned char i, j;
	
	for(j=0; j<NO_OBJ_TYPES; j++)
	{
		for(i=0; i<MAX_OBJECTS; i++)
			obj_free[j][i] = 0;
		
		num_active[j] = 0;
	}
	
	for(i=0; i<MAX_EXPLOSIONS; i++)
		exp_free[i] = 0;
		
	no_objects = 0;
	am_i_dead = 0;
}

void CreateExplosion(object *obj)
/*
Cool routine to blow an object into it's polygons.  Takes a while though,
so I'll have to fix it up a bit.  Looks damn nice though...
*/
{
	register int i, j = 0;
	register polygon *pg = obj->poly;
	
	while(pg)
	{
		for(i=j; i<MAX_EXPLOSIONS; i++)
		{
			if(!exp_free[i]) /* is free ! */
			{
				register int n;
				
				explosions[i].lastdrawme = explosions[i].lastdraw2 = 0;
				explosions[i].drawme = 1;
				explosions[i].explode = 0;
				explosions[i].i_am_dying = 0;
				explosions[i].type = EXPLOSION;
				explosions[i].centre_x = pg->centre.x;
				explosions[i].centre_y = pg->centre.y;
				explosions[i].centre_z = pg->centre.z;
				explosions[i].timeinflight = getrandom(5,25); /* time will last */
				
				explosions[i].poly->numpoints = explosions[i].numpoints =
					pg->numpoints;
				
				explosions[i].poly->colour = pg->colour;
				explosions[i].poly->centre = pg->centre;
				
				for(n=0; n<pg->numpoints; n++)
					explosions[i].poly->p[n] = n + 1;
					
				for(n=1; n<=pg->numpoints; n++)
					explosions[i].objpoints[n] = obj->objpoints[pg->p[n-1]];
				
				Translate_Obj_Abs(&explosions[i], obj->trans_x,
					obj->trans_y, obj->trans_z);
					
				Rotate_Obj_Abs(&explosions[i], obj->rot_x,
					obj->rot_y, obj->rot_z);
				
				explosions[i].velocity = getrandom(0,10);
				explosions[i].heading = getrandom(0,360);
				explosions[i].up_or_down = getrandom(0,2);
				
				exp_free[i] = 1; /* not free */
				active_list[no_objects] = &explosions[i];
				no_objects++;
				break;	
			}
		}
		j = i;	/* by doing this, I can search in order n instead of n^2 */
		pg = (polygon *) pg->next;
	}
	StartSound(1);
}
					
/* end of module */
