/*

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

Black Nebula

File :				enemy.c
Programmer:		Colin Adams
Date:				6/5/91
Last Modified :	10/6/91

Description:

Updates the enemy objects in space (movement and rotations)
Will be fixed a lot for the next version.

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

*/

#include "3d.h"

extern short U_rot;

#define DecreaseVelocity(obj) { if(obj->velocity>4)obj->velocity--; }

#define IncreaseMissileVelocity(obj) {if(obj->velocity<30)obj->velocity++;}
#define DecreaseMissileVelocity(obj) {if(obj->velocity>20)obj->velocity--;}

extern void AddNewObject(short, short, short, short);
extern int player_missiles;

int player_scale, player_score;
short num_active[NO_OBJ_TYPES];

/*	------------------------------------------------------------------
		Coding
		------------------------------------------------------------------
*/

void IncreaseVelocity(object *obj)
/* some ships move much faster than others.... */
{
	register int max;
	
	switch(obj->type)
	{
		case SHUTTLE: max = 5; break;
		case KRAIT: max = 11; break;
		case GECKO: max = 8; break;
		case THARGOID: max = 11; break;
		case MAMBA: max = 8; break;
	}
	
	if(obj->velocity<max) obj->velocity++;
}

int GetTargetAngle(object *obj, short px, short pz)
{
	register int o, angle, tempint;
	register char quad;
	int temp, ex = obj->trans_x+obj->centre_x, ez = obj->trans_z+obj->centre_z;
	float depth, temp2;
	int d;
	
	tempint = px-ex;
	depth = tempint * tempint;
	tempint = pz-ez;
	depth += tempint * tempint;

	if(ex>px)
	{
		o = ex - px;
		if(ez>pz)
			quad = 4;
		else
		{
			if(pz==ez) return 0;
			quad = 1;
		}
	}
	else
	{
		if(px==ex)
		{
			if(ez<pz)
				return 90;
			else
				return 270;
		}
		
		o = px - ex;
		
		if(ez>pz)
			quad = 3;
		else
		{
			if(pz==ez) return 180;
			quad = 2;
		}
	}
	
	temp = o * 4096;
	temp2 = fsqrt(depth);
	
	d = temp/temp2;
	
	if(d>4096) d = 4096; /* to fix small errors */
	
	angle = anti_sin[d];
	
	switch(quad)
	{
		case 1: angle = 90 - angle; break;
		case 2: angle = 90 + angle; break;
		case 3: angle = 180 + (90-angle); break;
		case 4: angle += 270; break;
	}
	return angle;
}

int GetPointAngle(int *de, short px, short pz)
{
	register int o, angle, tempint;
	register char quad;
	int temp;
	float depth, temp2;
	int d;
	
	tempint = px-ex;
	depth = tempint * tempint;
	tempint = pz-ez;
	*de = (depth += tempint * tempint);

	if(ex>px)
	{
		o = ex - px;
		if(ez>pz)
			quad = 4;
		else
		{
			if(pz==ez) return 0;
			quad = 1;
		}
	}
	else
	{
		if(px==ex)
		{
			if(ez<pz)
				return 90;
			else
				return 270;
		}
		
		o = px - ex;
		
		if(ez>pz)
			quad = 3;
		else
		{
			if(pz==ez) return 180;
			quad = 2;
		}
	}
	
	temp = o * 4096;
	temp2 = fsqrt(depth);
	
	d = temp/temp2;
	
	if(d>4096) d = 4096; /* to fix small errors */
	
	angle = anti_sin[d];
	
	switch(quad)
	{
		case 1: angle = 90 - angle; break;
		case 2: angle = 90 + angle; break;
		case 3: angle = 180 + (90-angle); break;
		case 4: angle += 270; break;
	}
	return angle;
}

void LaunchMissile(object *obj)
/* this object would like to launch a missile at the player... */
{
	register int i;
	int tx = obj->centre_x + obj->trans_x + FastCos(200, obj->heading);
	int tz = obj->centre_z + obj->trans_z - FastSin(200, obj->heading);
			
	for(i=0; i<MAX_OBJECTS; i++)
	{
		register object *missile;
			
		if(!obj_free[ENEMYMISSILE][i])
		{
			int difficult = player_score>>12;
			register int rand = getrandom(0, 30-difficult) - (30-difficult)/2;
			int heading = obj->heading + rand;
			
			if(heading<0)
				heading += 360;
			else if(heading>359)
				heading -= 360;
			
			missile = &obj_types[ENEMYMISSILE][i];
			Translate_Obj_Abs(missile, tx, obj->trans_y+obj->centre_y, tz);
			missile->i_am_dying = missile->lastdrawme = missile->lastdraw2 = 0;
			missile->velocity = 25 + difficult;
			missile->timeinflight = 0;
			missile->heading = heading;
			Rotate_Obj_Abs(missile, 0, heading, 0);
			missile->explode = 0;
			obj_free[ENEMYMISSILE][i] = 1;
			active_list[no_objects] = missile;
			no_objects++;
			num_active[ENEMYMISSILE]++;
			StartSound(0);
			return;
		}
	}
}
		
void MoveObjects(void)
/* horrible sprawling routine... should be broken up */
{
	register int i;
	register object *obj;
		
	for(i=0; i<no_objects; i++)
	{
		obj = active_list[i];
		
		if(obj->i_am_dying)
			continue;
		else
			obj->drawme = 1;
		
		switch(obj->type)
		{
			case FIXED:
				Rotate_Obj_Rel(obj, 2, 2, 2);
				break;
			case PLAYER:
				break;
			case ENEMYMISSILE:
			{
				register int i;
				register object *target;
				
				if(obj->timeinflight++>40)
				{
					obj->i_am_dying = 1;
					obj->explode = 1;
					break;
				}
				
				obj->trans_x += FastCos(obj->velocity, obj->heading);
				obj->trans_z -= FastSin(obj->velocity, obj->heading);

				if(obj->trans_x<0)
					obj->trans_x = MAX_WORLD_X;
				else if(obj->trans_x > MAX_WORLD_X)
					obj->trans_x = 0;
			
				if(obj->trans_z<0)
					obj->trans_z = MAX_WORLD_Z;
				else if(obj->trans_z>MAX_WORLD_Z)
					obj->trans_z = 0;
			
				/* check if missile hit something */
				
				for(i=0; i<no_objects; i++)
				{
					target = active_list[i];
					
					if(target->type==MYMISSILE)
					{
						register int temp, distance;
						temp = (target->trans_x + target->centre_x) -
								(obj->trans_x + obj->centre_x);
						
						distance = temp * temp;

						temp = (target->trans_y + target->centre_y) -
								(obj->trans_y + obj->centre_y);
						
						distance += temp * temp;
						
						temp = (target->trans_z + target->centre_z) -
								(obj->trans_z + obj->centre_z);
					
						distance += temp * temp;
						
						if(distance<(obj->radius + 2000))
						{
							obj->i_am_dying = target->i_am_dying = 1;
							obj->explode = target->explode = 1;
							StartSound(2);
							break;
						}
					}
				}
				Rotate_Obj_Rel(obj, 5, 0, 0);
				break;
			}
			case MYMISSILE:
			{
				register int i;
				register object *target;
				
				if(obj->timeinflight++>50)
				{
					obj->i_am_dying = 1;
					obj->explode = 1;
					break;
				}
				
				obj->trans_x += FastCos(obj->velocity, obj->heading);
				obj->trans_z -= FastSin(obj->velocity, obj->heading);

				if(obj->trans_x<0)
					obj->trans_x = MAX_WORLD_X;
				else if(obj->trans_x > MAX_WORLD_X)
					obj->trans_x = 0;
			
				if(obj->trans_z<0)
					obj->trans_z = MAX_WORLD_Z;
				else if(obj->trans_z>MAX_WORLD_Z)
					obj->trans_z = 0;
			
				/* check if missile hit something */
				
				for(i=0; i<no_objects; i++)
				{
					target = active_list[i];
					
					if(target->type<=MAMBA)
					{
						register int temp, distance;
						temp = (target->trans_x + target->centre_x) -
								(obj->trans_x + obj->centre_x);
						
						distance = temp * temp;

						temp = (target->trans_y + target->centre_y) -
								(obj->trans_y + obj->centre_y);
						
						distance += temp * temp;
						
						temp = (target->trans_z + target->centre_z) -
								(obj->trans_z + obj->centre_z);
						
						distance += temp * temp;
						
						if(distance<(obj->radius + 2000))
						{
							obj->i_am_dying = target->i_am_dying = 1;
							obj->explode = target->explode = 1;
							
							switch(target->type)
							{
								case FIXED: player_score += 1000; break;
								case THARGOID: player_score += 1500; break;
								case ENEMYMISSILE: player_score += 10; break;
								case KRAIT: player_score += 600; break;
								case MAMBA: player_score+= 1000;
								default: player_score += 300;
							}
							
							{
								register int temp = player_score>>12;
								if(temp>player_scale)
								{
									player_scale = temp;
									player_missiles++;
								}
							}
							
							StartSound(2);
							break;
						}
					}
				}
				Rotate_Obj_Rel(obj, 5, 0, 0);
				break;
			}
			case GUIDEDMISSILE:
			{
				register short dest, fdest, theight, height, s1, s2, i;
				register object *target;
				
				Rotate_Obj_Rel(obj, 5, 0, 0);
				
				if(obj->timeinflight++>100)
				{
					obj->i_am_dying = 1;
					obj->explode = 1;
					break;
				}
				
				if(obj->target==NULL) /* no target yet */
				{
					register int sh_dist = 0x7fffffff; /* a really big number */
					
					for(i=0; i<no_objects; i++)
					{
						if(active_list[i]->type<=MAMBA &&
							active_list[i]->type>ENEMYMISSILE)
						{
							register int temp, distance;
							register object *target = active_list[i];
							
							temp = (target->trans_x + target->centre_x) -
								(obj->trans_x + obj->centre_x);
						
							distance = temp * temp;

							temp = (target->trans_y + target->centre_y) -
								(obj->trans_y + obj->centre_y);
						
							distance += temp * temp;
						
							temp = (target->trans_z + target->centre_z) -
								(obj->trans_z + obj->centre_z);
						
							distance += temp * temp;
				
							if(distance<sh_dist)
							{
								obj->target = active_list[i];
								sh_dist = distance;
							}
						}
					}
				}
				
				target = (object *) obj->target;

				{
					register char flag = 0;
					for(i=0; i<no_objects; i++)
					{
						if(active_list[i]==target)
						{
							flag = 1;
							break;
						}
					}
					if(!flag)
					{
						obj->target = NULL; /* get a new target next frame */
						break;
					}
				}
				
				/* move missile */
				
				height = obj->trans_y + obj->centre_y;
				
				theight = target->trans_y + target->centre_y;
				
				if(theight>height && height<MAX_WORLD_Y-500)
					obj->trans_y += 5;
				else if(height>5)
					obj->trans_y -= 5;

				dest = GetTargetAngle(target, obj->trans_x+obj->centre_x,
				 obj->trans_z+obj->centre_z);
				
				if(dest<0)
					break;

				s1 = dest - obj->heading; /* get +ve diff. heading */
				s2 = obj->heading + 360 - dest;
				
				fdest = abs(s1)>abs(s2) ? -s2 : s1;

				if(fdest>180)
				{
					DecreaseMissileVelocity(obj);
				}
				else
				{
					IncreaseMissileVelocity(obj);
				}
				
				if(fdest>0)
				{
					if(fdest>5)
						fdest = 5;
					
					Rotate_Obj_Rel(obj, 0, fdest, 0);
					obj->heading += fdest;
				}
				else
				{
					if(fdest<-5)
					{
						obj->heading -= 5;
						Rotate_Obj_Rel(obj, 0, -5, 0);
					}
					else
					{
						Rotate_Obj_Rel(obj, 0, fdest, 0);
						obj->heading += fdest;
					}
				}
				
				if(obj->heading>359)
					obj->heading -= 360;
				else if(obj->heading<0)
					obj->heading += 360;
				
				obj->trans_x += FastCos(obj->velocity, obj->heading);
				obj->trans_z -= FastSin(obj->velocity, obj->heading);

				if(obj->trans_x<0)
					obj->trans_x = MAX_WORLD_X;
				else if(obj->trans_x > MAX_WORLD_X)
					obj->trans_x = 0;
			
				if(obj->trans_z<0)
					obj->trans_z = MAX_WORLD_Z;
				else if(obj->trans_z>MAX_WORLD_Z)
					obj->trans_z = 0;
					
				/* now check if the target was hit! */
				
				{
					register int temp, distance;
					
					temp = (target->trans_x + target->centre_x) -
							(obj->trans_x + obj->centre_x);
						
					distance = temp * temp;

					temp = (target->trans_y + target->centre_y) -
								(obj->trans_y + obj->centre_y);
						
					distance += temp * temp;
						
					temp = (target->trans_z + target->centre_z) -
							(obj->trans_z + obj->centre_z);
						
					distance += temp * temp;
						
					if(distance<(obj->radius + 2500))
					{
						obj->i_am_dying = target->i_am_dying = 1;
						obj->explode = target->explode = 1;
							
						switch(target->type)
						{
							case FIXED: player_score += 1000; break;
							case THARGOID: player_score += 1500; break;
							case ENEMYMISSILE: player_score += 10; break;
							case KRAIT: player_score += 600; break;
							case MAMBA: player_score+= 1000;
							default: player_score += 300;
						}
						
						{
							register int temp = player_score>>12;
							if(temp>player_scale)
							{
								player_scale = temp;
								player_missiles++;
							}
						}
							
						StartSound(2);
					}
				}		
				break;
			}
			case EXPLOSION: /* floats away */
			{
				int rad;
				
				if((--(obj->timeinflight))==0)
				{
					obj->i_am_dying = 1;
					obj->explode = 0;
					break;
				}
				
				rad = getrandom(0,10);
				
				if(obj->up_or_down)
				{
					if(obj->trans_y<MAX_WORLD_Y-100)
						obj->trans_y += rad;
				}
				else
				{
					if(obj->trans_y>100)
						obj->trans_y -= rad;
				}
				
				if(rad>5)
					rad = getrandom(1, 6);
				else
					rad = -getrandom(1, 6);
				
				Rotate_Obj_Rel(obj, rad+2, rad-1, rad+1);
				
				obj->trans_x += FastCos(obj->velocity, obj->heading);
				obj->trans_z -= FastSin(obj->velocity, obj->heading);

				if(obj->trans_x<0)
					obj->trans_x = MAX_WORLD_X;
				else if(obj->trans_x > MAX_WORLD_X)
					obj->trans_x = 0;
			
				if(obj->trans_z<0)
					obj->trans_z = MAX_WORLD_Z;
				else if(obj->trans_z>MAX_WORLD_Z)
					obj->trans_z = 0;
				break;
			}
			default: /* enemy ship combat algorithm... */
			{
				register short dest, fdest, height, s1, s2;
				int distance;
				
				if(obj->type==THARGOID)
					Rotate_Obj_Rel(obj, 0, 5, 0);
				else if(obj->type==KRAIT)
					Rotate_Obj_Rel(obj, 5, 0, 0);
				
				height = obj->trans_y + obj->centre_y;
				
				fdest = ey - height;
				fdest = abs(fdest);
				
				if(ey>height && height<MAX_WORLD_Y-500)
				{
					if(fdest>5)
						obj->trans_y += 5;
					else
						obj->trans_y += fdest;
				}
				else if(height>5)
				{
					if(fdest>5)
						obj->trans_y -= 5;
					else
						obj->trans_y -= fdest;
				}

				dest = GetPointAngle(&distance,obj->trans_x, obj->trans_z);
				
				if(dest<0)
					break;

				s1 = dest - obj->heading; /* get +ve diff. heading */
				s2 = obj->heading + 360 - dest;
				
				fdest = abs(s1)>abs(s2) ? -s2 : s1;

				if(fdest>180)
				{
					DecreaseVelocity(obj);
				}
				else
				{
					IncreaseVelocity(obj);
				}
				
				if(fdest>0)
				{
					if(fdest>5)
						fdest = 5;
					
					Rotate_Obj_Rel(obj, 0, fdest, 0);
					obj->heading += fdest;
				}
				else
				{
					if(fdest<-5)
					{
						obj->heading -= 5;
						Rotate_Obj_Rel(obj, 0, -5, 0);
					}
					else
					{
						Rotate_Obj_Rel(obj, 0, fdest, 0);
						obj->heading += fdest;
					}
				}
				
				if(abs(fdest)<10 && distance<(2500*2500))
				{
					if(obj->type==THARGOID)
					{
						if(getrandom(0, 20)==0)
							LaunchMissile(obj);
					}
					else if(obj->type==MAMBA)
					{
						if(getrandom(0,25)==0)
							LaunchMissile(obj);
					}
					else if(obj->type==KRAIT)
					{
						if(getrandom(0,35)==0)
							LaunchMissile(obj);
					}
					else 
					{
						if(getrandom(0,45)==0)
							LaunchMissile(obj);
					}
				}
						
				
				if(obj->heading>359)
					obj->heading -= 360;
				else if(obj->heading<0)
					obj->heading += 360;
				
				obj->trans_x += FastCos(obj->velocity, obj->heading);
				obj->trans_z -= FastSin(obj->velocity, obj->heading);

				if(obj->trans_x<0)
					obj->trans_x = MAX_WORLD_X;
				else if(obj->trans_x > MAX_WORLD_X)
					obj->trans_x = 0;
			
				if(obj->trans_z<0)
					obj->trans_z = MAX_WORLD_Z;
				else if(obj->trans_z>MAX_WORLD_Z)
					obj->trans_z = 0;
			
					break;
			}
		}
	}
}

void UpdateObjects(void)
/*
Creates new objects, adding them to the game dynamically
*/
{
	if(!num_active[FIXED]) /* space stations */
	{
		/* there are none left, so I'll create 4 new ones ... */
		
		AddNewObject(FIXED, 500, 1000, 500);
		AddNewObject(FIXED, MAX_WORLD_Z-500, 1000, 500);
		AddNewObject(FIXED, 500, 1000, MAX_WORLD_Z-500);
		AddNewObject(FIXED, MAX_WORLD_Z-500, 1000, MAX_WORLD_Z-500);
	}
	
	if(num_active[3]+num_active[4]+num_active[5]+num_active[6]+
	num_active[2]!=3)
	{
		switch(getrandom(0,3))
		{
			case 0:
			AddNewObject(getrandom(SHUTTLE,MAMBA), getrandom(600,7000), 1000, MAX_WORLD_Z-100);
			break;
			case 1:
			AddNewObject(getrandom(SHUTTLE,MAMBA), getrandom(600,7000), 1000, 100);
			break;
			case 2:
			AddNewObject(getrandom(SHUTTLE,MAMBA), MAX_WORLD_X-200, 1000, getrandom(600,7000));
			break;
			case 3:
			AddNewObject(getrandom(SHUTTLE,MAMBA), 100, 1000, getrandom(600,7000));
			break;
		}
	}	
}

/* end of module enemy.c */
