// Quake style demo : Step 3
//
// In this step we will add the opponent


#include "mrft_api.h"

#include <stdio.h>
#include <math.h>


//We use two global variable gl_player & gl_opponent
//all global data was put inside these structures
//For CPP object oriented programming one could replace these structurs
//with two classes and put also all the specific functions of the player \ oppenent inside
//the relevant class class.
typedef struct player_data {
	RECT window_rect; //The size of the window used for rendering
	HPEN  crosshair_pen; //The pen used for drawing the crosshair (WIN32 stuff. Nothing to do with Morfit ...)
	DWORD weapon; //A handle to the weapon object
	DWORD fire_animation; //A handle to the animation used for shooting
        DWORD fire_effect_polygon; // When shooting we enable this polygon to be used by the shooting-fire-animation
	DWORD weapon_idle_sequence; //The sequence that is used with the weapon 3D animation when not shooting
	DWORD weapon_shooting_sequence; //A handle to the 3d animation sequance that is used while shooting
	DWORD weapon_shooting_mark_bitmap; //A handle to the the bitmap we use for marking shooting holes

	DWORD shooting_shred; //A object handle. This is the master splinter. When we shoot we duplicate this object
						  //for creating the broken pieces falling down from where the bullets hit
	int   is_shooting;
} player_data;


typedef struct opponent_data {
	DWORD handle; //the handle to the oppenent object
	double height;
	double opponent_width;
	double speed;
	int		stage;
	int		got_hit_flag;
        int		number_of_times_got_hit;
	DWORD stand_sequence;
	DWORD run_sequence;
	DWORD attack_sequence;
	DWORD pain_sequence;
	DWORD jump_sequence;
	DWORD flip_sequence;
	DWORD salute_sequence;
	DWORD taunt_sequence;
	DWORD wave_sequence;
	DWORD point_sequence;
	DWORD cdeath_sequence;
	DWORD death_sequence;
} opponent_data;

player_data		gl_player;
opponent_data	gl_opponent;

//Constants that one can modify
//-----------------------------

//The constants bellow have nothing with the Morfit Engine.
//These constants are used only by the program in this page
//Try modifing values and see how it effect the program


#define STEP 60 //The number of units that we move forward.
				//Increase / decrease this number to move faster \ slower


#define GRAVITY 100.0  //Determins the speed in which the player falls down
					  //Increase / Decrease to fall faster \ slower

#define HEIGHT_ABOVE_GROUND (50.0*WORLD_FACTOR) //The height of the player above the ground

#define VIEWER_SIZE  (50*WORLD_FACTOR) //The width and length of the player. This determins how near we can get to a wall. If this constant is too big we might not be able
						//to go through narrow doors. 

//The walking effect is the change in the player height while walking.
#define SIZE_OF_WALKING_EFFECT 32 // The number of renders needed to complete one cycle of walking. This number shoud
								  // be a multiplication of 4 (because of the 4 steps sound)
#define WALKING_EFFECT_HEIGHT (HEIGHT_ABOVE_GROUND*0.125) // The maximum chang of the player altitude while walking

#define MOUSE_SENSITIVITY 0.2 //setting a bigger number will make the mouse more sensitive
#define MAX_TILT 85 //The maximum angle that we can look up or down

#define GUN_MOVING_SPEED 0.05  //A number bigger than 0 and smaller than 1
							   //When moving the player viewing direction the gun follows.
							   //Setting a bigger number will make the gun follow faster the player viewing angle

#define SHOOTING_MARK_RADIUS 20 //The size of the spot that the bullets lieve on the walls

#define SHRED_SPEED 20 //The speed in which the pieces of stone are flying when we shoot at them
#define SHRED_SIZE 10 //The size of the fractions that fly when shooting

#define WORLD_FACTOR 6 //So it is easy to change many parameters when using a biger or smaller world



//Constants that one can NOT modify
//-----------------------------
//When the player stands there is now change in the altitude of the camera
#define STANDING 1 
#define WALKING  2
#define FALLING  3

#define SOUND_STEP1 0
#define SOUND_STEP2 1
#define SOUND_STEP3 2
#define SOUND_STEP4 3
#define SOUND_FALL  4
#define SOUND_HIT_WALL 5
#define SOUND_SHOOT 6
#define NUMBER_OF_SOUNDS 7

//Opponent stage constant
#define OPPONENT_IDLE 1
#define OPPONENT_ATTACK 2

#define OPPONENT_ATTACK_DISTANCE 1000

DWORD g_skyArray[100];


//This is the starting point of the program
//and the main loop. The parameters that this function gets
//have nothing to do with Morfit. This is the starting point of every Win32 
//application. Note that it is better to make a WIN32 application ( starting with the  WinMain() function)
//rather than a Consol Application (starting with main() )
//(among other things one doesn't get the undesired dos window)
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR     lpCmdLine,  int nCmdShow)
{
	//functions that we use here
	//--------------------------
	int initialize(double walking_height_effect[], DWORD sounds[]);
	void camera_update_location(DWORD camera, double walking_height_effect[], int *player_stage, int *player_stage_progress, DWORD sounds[]);
	void weapon_update(DWORD camera, DWORD sounds[]);
	void opponent_update(opponent_data *opponent);
	void draw_crosshair(HDC dc_handle, DWORD camera);
	void move_sky();
	
	//variables:
	//-----------
	DWORD sounds[NUMBER_OF_SOUNDS];
	
	double walking_height_effect[SIZE_OF_WALKING_EFFECT];
	int player_stage=STANDING;
	int player_stage_progress=0;

	
	//The function itself:
	//--------------------
	//
	if(initialize(walking_height_effect, sounds)==VR_ERROR) return(VR_ERROR);
	DWORD camera=Morfit_camera_get_default_camera();


	//Loops till one presses the Escape key (GetAsyncKeyState() is a WIN32 function )
	while( (GetAsyncKeyState(VK_ESCAPE)&1) ==0 ) {
		
		//Instead of Morfit_engine_render() we will use
		//Morfit_engine_render_on_dc() so we can draw on top of the
		//rendered window.
		HDC dc_handle=Morfit_engine_render_on_dc(NULL,camera);	
		//Draw the crosshair
		draw_crosshair(dc_handle, camera);
		Morfit_engine_copy_image_from_dc_to_screen();


		//Updates the camera location according to the keyboard input 
		camera_update_location(camera, walking_height_effect, &player_stage, &player_stage_progress, sounds);

		weapon_update(camera, sounds);

		
		opponent_update(&gl_opponent);

		move_sky();
	}
	
	//Should always be called before exiting the program
	Morfit_engine_close();

	return(OK);
}


//Some very simple functions for handling vectors
//-----------------------------------------------

void copy_vector(double dst[3], double src[3])
{
	dst[0]=src[0];
	dst[1]=src[1];
	dst[2]=src[2];
}

void set_vector(double dst[3], double x, double y, double z)
{
	dst[0]=x;
	dst[1]=y;
	dst[2]=z;
}


void add_vector(double dst[3], double src[3])
{
	dst[0]+=src[0];
	dst[1]+=src[1];
	dst[2]+=src[2];
}

void sub_vector(double dst[3], double src[3])
{
	dst[0]-=src[0];
	dst[1]-=src[1];
	dst[2]-=src[2];
}


void mul_vector(double vec[3], double factor)
{
	vec[0]*=factor;
	vec[1]*=factor;
	vec[2]*=factor;
}

//Check whether the player is on air (no floor bellow) and we have to fall down
void camera_manage_free_falling(DWORD camera, double walking_height_effect[], int *player_stage, int *player_stage_progress, DWORD sounds[])
{
	
	//First we will find the ground beneath the camera.
	//In order to do so we will do the following:
	//We will do a collision checking between the current location and 
	// a point bellow us. The collision check should return a point on the floor
	//bellow us. 
	double location[3], bellow[3], intersection[3];
	Morfit_camera_get_location(camera, &location[0], &location[1], &location[2]);
	copy_vector(bellow, location);
	bellow[2]=-1e10; // -1e10 is the same as -10000000000. Any big negative number would do the job.
	DWORD poly,obj;
	
	//We will now check collision between our current position and a point bellow us
	int rc=Morfit_engine_is_movement_possible(location, bellow, &poly, intersection, &obj);
	if(rc==YES) {
		//ERROR we are walking on air !
		//There is nothing bellow ! 
	}
	else {
		//We will now set the camera in the desired height above the ground
		//The desired height is HEIGHT_ABOVE_GROUND+walking_effect
		//If we are currently falling down we will only decrease the height by GRAVITY
		double walking_effect=0;
		if( *player_stage==WALKING) 
			walking_effect=walking_height_effect[*player_stage_progress];
		double height=location[2]-intersection[2];
		double wanted_height_above_ground=HEIGHT_ABOVE_GROUND+walking_effect;
		
		//sound effects
		if(height>wanted_height_above_ground+GRAVITY) {
			*player_stage=FALLING;
			location[2]-=GRAVITY;
			height-=GRAVITY;
		}
		else {
			location[2]=intersection[2]+wanted_height_above_ground;
			if(*player_stage==FALLING) {
				*player_stage=STANDING;
				Morfit_sound_play(sounds[SOUND_FALL], SOUND_NO_LOOP);
			}
		}
		


		Morfit_camera_set_location(camera, location[0], location[1], location[2]);

	}

}

//Moves the camera according to keyboard input.
void camera_move_according_to_keyboard_input(DWORD camera, int *player_stage, int *player_stage_progress, DWORD sounds[])
{

	//Check whether the left or the right error keys are pressed
	if(GetAsyncKeyState(VK_LEFT)<0) Morfit_camera_rotate_z(camera, 5,WORLD_SPACE);
	if(GetAsyncKeyState(VK_RIGHT )<0) Morfit_camera_rotate_z(camera, -5,WORLD_SPACE);

	double direction[3];
	Morfit_camera_get_direction(camera, &direction[0], &direction[1], &direction[2]);
	direction[2]=0; //We want to move only on XY plane (not up or down)
	if(Morfit_math_normalize_vector(direction)==VR_ERROR){
		//ERROR happens only if we look totally up or totally down (i.e direction==0,0,1)
		//should never happen
		set_vector(direction, 1,0,0);
	}

	
	if((*player_stage!=FALLING) && (GetAsyncKeyState(VK_UP)<0)) {
		*player_stage=WALKING; 
		mul_vector(direction,STEP);
		
		//Advance the progress counter of the walking height effect
		(*player_stage_progress)++;
		if(*player_stage_progress>=SIZE_OF_WALKING_EFFECT) {
			*player_stage_progress=0;
		}

		//Make the right sound if needed
		if( ((*player_stage_progress) % (SIZE_OF_WALKING_EFFECT/4))==0) {
			//Time to play a sound
			int sound_num=(*player_stage_progress)/(SIZE_OF_WALKING_EFFECT/4);
			Morfit_sound_play(sounds[sound_num], SOUND_NO_LOOP);
		}

	}
	else
	if((*player_stage!=FALLING) && (GetAsyncKeyState(VK_DOWN)<0)) {
		mul_vector(direction,-STEP);
		*player_stage=WALKING;
		
		//Advance back progress counter of the walking height effect
		(*player_stage_progress)--;
		if((*player_stage_progress)<0) *player_stage_progress=SIZE_OF_WALKING_EFFECT-1;

		//Make the right sound if needed
		if( ((*player_stage_progress) % (SIZE_OF_WALKING_EFFECT/4))==0) {
			//Time to play a sound
			int sound_num=(*player_stage_progress)/(SIZE_OF_WALKING_EFFECT/4);
			Morfit_sound_play(sounds[sound_num], SOUND_NO_LOOP);
		}


	}
	else {
		//If nor forward nor backward keys were pressed then the player is standing .
		if(*player_stage!=FALLING)
			*player_stage=STANDING;
		return;
	}



	double wanted_location[3];
	Morfit_camera_get_location(camera, &wanted_location[0], &wanted_location[1], &wanted_location[2]);
	add_vector(wanted_location, direction);


	//This is a new function that was intreduced in version 5.4
	//It is very useful for moving the camera withough going through walls.
	int did_camera_move=Morfit_camera_move_with_collision_detection(camera, wanted_location, VIEWER_SIZE);	
	if(did_camera_move==NO) {
		if(*player_stage_progress==0) //So it want play each render
			Morfit_sound_play(sounds[SOUND_HIT_WALL], SOUND_NO_LOOP);
	}


}

void camera_move_according_to_mouse_input(DWORD camera)
{
	DWORD static		first_time_flag=YES;
	static POINT		point;
	
	if(GetCursorPos( &point)==0) 
		return; //the function failed

	int center_x=(gl_player.window_rect.right-gl_player.window_rect.left)/2;
	int center_y=(gl_player.window_rect.bottom-gl_player.window_rect.top)/2;
	
	//Not that setting the cursor position to the center can 
	//be very annoying if you are debugging your code
	//It is recommended to cancel the call to camera_move_according_to_mouse_input()
	//while debugging your code
	SetCursorPos(center_x, center_y);
	
	if(first_time_flag==YES) {
		first_time_flag=NO; //At the first time we just put the cursor at the center
		return;
	}

	int diff_x=point.x-center_x;
	int diff_y=point.y-center_y;
	if(diff_x!=0)
		Morfit_camera_rotate_z(camera, (double)-diff_x*MOUSE_SENSITIVITY,WORLD_SPACE);

	if(diff_y==0) return;
	double tilt=Morfit_camera_get_tilt(camera);
	tilt-= (double)diff_y*MOUSE_SENSITIVITY;
	if(tilt>MAX_TILT) tilt=MAX_TILT;
	if(tilt<-MAX_TILT) tilt=-MAX_TILT;
	Morfit_camera_set_tilt(camera, tilt);
	

}


void camera_update_location(DWORD camera, double walking_height_effect[], int *player_stage, int *player_stage_progress, DWORD sounds[])
{
	camera_manage_free_falling(camera, walking_height_effect, player_stage, player_stage_progress, sounds);

	camera_move_according_to_keyboard_input(camera, player_stage, player_stage_progress, sounds);

	//Cancel the line bellow when debugging so it wont change the mouse position.
	camera_move_according_to_mouse_input(camera);

}


void intitialize_walking_height_effect_array(double walking_height_effect[])
{
	double step=2*M_PI/SIZE_OF_WALKING_EFFECT;
	double angle=0;
	for(int i=0; i<SIZE_OF_WALKING_EFFECT; i++) {
		walking_height_effect[i]=sin(angle)*WALKING_EFFECT_HEIGHT;
		angle+=step;
	}

}


void load_sounds(DWORD sounds[])
{
	for(int i=0; i<4; i++) {
		char file_name[1000];
		sprintf(file_name,"..\\Worlds\\sound\\step%d.wav",i+1);
		sounds[i]=Morfit_sound_load(file_name, NULL, SOUND_DISTANCE_DEFAULT);
	}

	sounds[SOUND_FALL]=Morfit_sound_load("..\\Worlds\\sound\\fall1.wav", NULL, SOUND_DISTANCE_DEFAULT);
	sounds[SOUND_HIT_WALL]=Morfit_sound_load("..\\Worlds\\sound\\hit_wall.wav", NULL, SOUND_DISTANCE_DEFAULT);
	sounds[SOUND_SHOOT]=Morfit_sound_load("..\\Worlds\\sound\\shoot.wav", NULL, SOUND_DISTANCE_DEFAULT);

}

void find_a_point_at_the_end_of_the_weapon(DWORD all_polygons[], int num_of_polygons, double result[3])
{
	double min_point=999999999;
	set_vector(result,0,0,0);

	for(int i=0; i<num_of_polygons; i++) {
		for(DWORD point=Morfit_polygon_get_first_point(all_polygons[i]); point!=NULL; point=Morfit_point_get_next_point(point)) {
			double xyz[3];
			Morfit_point_get_xyz(point, xyz);
			if(xyz[0]<min_point) {
				copy_vector(result, xyz);
				min_point=xyz[0];
			}
		}
	}

}

//Creates the polygon that is used the create the fire effect 
//when shooting
void create_the_weapon_fire_effect(void)
{

	gl_player.fire_effect_polygon=Morfit_polygon_create();

	//All the code bellow is simply to get the edge of the gun so we know where to
	//put the polygon with the fire effect
	int num_of_polygons=Morfit_object_get_number_of_polygons(gl_player.weapon);
	DWORD *all_polygons=(DWORD *)GlobalAlloc(GPTR, num_of_polygons*sizeof(DWORD));
	Morfit_object_get_all_polygons(gl_player.weapon, all_polygons);
	double edge_point[3];
	find_a_point_at_the_end_of_the_weapon(all_polygons, num_of_polygons, edge_point);
	GlobalFree((HGLOBAL)all_polygons);

	//Now lets build a square around the edge point
	double point1[6]={0,0,0,	0,0,  100};
	double point2[6]={0,0,0,	1,0,  100};
	double point3[6]={0,0,0,	1,1,  100};
	double point4[6]={0,0,0,	0,1,  100};
	double size=VIEWER_SIZE/10;
	set_vector(point1, edge_point[0], edge_point[1]-size, edge_point[2]+size);
	set_vector(point2, edge_point[0], edge_point[1]+size, edge_point[2]+size);
	set_vector(point3, edge_point[0], edge_point[1]+size, edge_point[2]-size);
	set_vector(point4, edge_point[0], edge_point[1]-size, edge_point[2]-size);

	
	Morfit_polygon_add_point(gl_player.fire_effect_polygon, point1);
	Morfit_polygon_add_point(gl_player.fire_effect_polygon, point2);
	Morfit_polygon_add_point(gl_player.fire_effect_polygon, point3);
	Morfit_polygon_add_point(gl_player.fire_effect_polygon, point4);
	
	Morfit_object_add_polygon(gl_player.weapon, gl_player.fire_effect_polygon);

	Morfit_polygon_set_animation(gl_player.fire_effect_polygon, gl_player.fire_animation);
	Morfit_polygon_set_translucent(gl_player.fire_effect_polygon,LIGHT_SOURCE_GLASS);

	//For the time being we will disable this polygon
	//When shooting we will enable it again
	Morfit_polygon_disable(gl_player.fire_effect_polygon);
	//Morfit_polygon_set_rotated(gl_player.fire_effect_polygon, YES);
}

DWORD create_master_shred(void)
{
	DWORD obj=Morfit_object_create("shred");

	DWORD poly=Morfit_polygon_create();
	
	double point1[6]={0,0,0,	0,0,  100};
	double point2[6]={0,0,0,	1,0,  100};
	double point3[6]={0,0,0,	1,1,  100};
	double point4[6]={0,0,0,	0,1,  100};
	double size=SHRED_SIZE;
	set_vector(point1, 0, -size, size);
	set_vector(point2, 0, size, size);
	set_vector(point3, 0, size, -size);
	set_vector(point4, 0, -size, -size);
	Morfit_polygon_add_point(poly, point1);
	Morfit_polygon_add_point(poly, point2);
	Morfit_polygon_add_point(poly, point3);
	Morfit_polygon_add_point(poly, point4);
	
	Morfit_object_add_polygon(obj, poly);

	Morfit_polygon_set_rotated(poly, YES);

	DWORD mask_bitmap=Morfit_bitmap_load("..\\worlds\\weapon\\mask.bmp",-1);
	Morfit_polygon_set_second_bitmap(poly, mask_bitmap);
	Morfit_polygon_set_translucent(poly, LIGHT_SOURCE_GLASS );
	
	Morfit_object_disable(obj);

	return(obj);

}


int load_weapon(void)
{

	gl_player.weapon = Morfit_object_create_from_file("..\\worlds\\weapon\\weapon2.md2");
	if(gl_player.weapon == NULL) 
		return(VR_ERROR);

	
	DWORD skin = Morfit_bitmap_load("..\\worlds\\weapon\\weapon2.bmp",-1);
	//Set the skin
	Morfit_object_set_bitmap(gl_player.weapon, skin);

	//The size of the weapon should fit the size of VIEWER_SIZE
	//so that the weapon does not go inside the walls
	double box[2][3];
	Morfit_object_get_bounding_box(gl_player.weapon, box);
	double weapon_size=box[1][0]-box[0][0];
	if(weapon_size==0) return(VR_ERROR); //for safty
	//We want the size to be exactly half of VIEWER_SIZE
	//so size*factor should be VIEWER_SIZE/2
	double factor=VIEWER_SIZE/(2*weapon_size);
	double scale[3] = {factor,factor,factor};
	Morfit_object_set_scale(gl_player.weapon, scale);


	//Setting the 3D animation sequence
	DWORD anim3d = Morfit_object_get_3D_animation(gl_player.weapon);
	if(anim3d != NULL) {
		//In every md2 model the names of the sequences are different
		//One should check the names of the models before using them
		gl_player.weapon_idle_sequence = Morfit_3D_sequence_get_using_name(anim3d, "idle");
		Morfit_object_set_3D_sequence(gl_player.weapon, gl_player.weapon_idle_sequence, 0);	
		gl_player.weapon_shooting_sequence=Morfit_3D_sequence_get_using_name(anim3d, "shot");
	}

	//Create the fire animation
	gl_player.fire_animation=Morfit_animation_create("shoot");
	DWORD frame1=Morfit_bitmap_load("..\\worlds\\weapon\\Mflash2.bmp",-1);
	DWORD frame2=Morfit_bitmap_load("..\\worlds\\weapon\\Mflash3.bmp",-1);
	DWORD frame3=Morfit_bitmap_load("..\\worlds\\weapon\\Mflash4.bmp",-1);

	Morfit_animation_add_bitmap(gl_player.fire_animation, BITMAP_LIST_FRONT, frame1, -1);
	Morfit_animation_add_bitmap(gl_player.fire_animation, BITMAP_LIST_FRONT, frame2, -1);
	Morfit_animation_add_bitmap(gl_player.fire_animation, BITMAP_LIST_FRONT, frame3, -1);

	DWORD times[3]={1,1,1};
	Morfit_animation_set_times(gl_player.fire_animation, times, 3);

	create_the_weapon_fire_effect();
	gl_player.is_shooting=NO;

	//Load the mark that the polygon leavs in the walls
	gl_player.weapon_shooting_mark_bitmap=Morfit_bitmap_load("..\\worlds\\weapon\\smark.bmp",-1);

	//When shooting at a wall it will cause pieces of the wall to fly (shreds)
	gl_player.shooting_shred=create_master_shred();

	return(OK);

}


//Check whether the player is on air (no floor bellow) and we have to fall down
void opponent_manage_free_falling(opponent_data *opponent)
{
	
	//First we will find the ground beneath the opponent.
	//In order to do so we will do the following:
	//We will do a collision checking between the current location and 
	// a point bellow us. The collision check should return a point on the floor
	//bellow us. 
	double location[3], bellow[3], intersection[3];
	Morfit_object_get_location(opponent->handle, &location[0], &location[1], &location[2]);
	copy_vector(bellow, location);
	bellow[2]=-1e10; // -1e10 is the same as -10000000000. Any big negative number would do the job.
	DWORD poly,obj;
	
	//We will now check collision between our current position and a point bellow us
	//We use Morfit_object_is_movement_possible() instead of Morfit_engine_is_movement_possible()
	//so that we wont get collision with the opponent body
	int rc=Morfit_object_is_movement_possible(opponent->handle, location, bellow, &poly, intersection, &obj);
	if(rc==YES) {
		//ERROR we are walking on air !
		//There is nothing bellow ! 
	}
	else {
		//We will now set the opponent in the desired height above the ground
		//The desired height is HEIGHT_ABOVE_GROUND+walking_effect
		//If we are currently falling down we will only decrease the height by GRAVITY
		double height=location[2]-intersection[2];
		double wanted_height_above_ground=opponent->height;
		
		if(height>wanted_height_above_ground+GRAVITY) {
			location[2]-=GRAVITY;
		}
		else {
			location[2]=intersection[2]+wanted_height_above_ground;
		}
		
		Morfit_object_set_location(opponent->handle, location[0], location[1], location[2]);

	}

}


int load_md2_object(char *md2_name, char *skin_bitmap, DWORD camera)
{
	char name[1000]; // 1000 is just to be sure it is big enough

	
	sprintf(name,"..\\Worlds\\md2 models\\%s.md2",md2_name);
	gl_opponent.handle = Morfit_object_create_from_file(name);
	 if(gl_opponent.handle == NULL) 
		return(VR_ERROR);

	
	sprintf(name,"..\\Worlds\\md2 models\\%s.bmp",skin_bitmap);
	DWORD skin = Morfit_bitmap_load(name,-1);
	//Set the skin
	Morfit_object_set_bitmap(gl_opponent.handle, skin);


	//Lets scale the opponent size so that he becomes the same size of the player
	//In other words the player viewing height == the camera height, should be at the height of
	//the opponent.
	double box[2][3];
	Morfit_object_get_bounding_box(gl_opponent.handle, box);
	double factor=HEIGHT_ABOVE_GROUND/(box[1][2]*2);
	
	double scale[3];
	set_vector(scale, factor, factor, factor);
	Morfit_object_set_scale(gl_opponent.handle, scale);

	//calculate the player height
	Morfit_object_get_bounding_box(gl_opponent.handle, box);
	//gl_opponent.height is the height from the middle of the opponent to the bottom of it
	//This is actually the total_height/2 (we scale it to be exactly HEIGHT_ABOVE_GROUND/2
	gl_opponent.height= -box[0][2]; // -box[0][2] is the size from the bottom of the object to the middle. 
								   // Note that box[1][2]== -box[0][2] since the object is centeralized
									//Note also that gl_opponent.height should be equal to HEIGHT_ABOVE_GROUND/2

	
	
	double width=box[1][0]-box[0][0];
	double length=box[1][1]-box[0][1];
	gl_opponent.opponent_width=width;
	if(length>width) gl_opponent.opponent_width=length;

	//Setting the 3D animation sequence
	DWORD anim3d = Morfit_object_get_3D_animation(gl_opponent.handle);
	if(anim3d != NULL) {
		//In every md2 model the names of the sequences are different
		//One should check the names of the models before using them
		//In order to make this code somewhat more general we will try
		//different names for each sequence.
		DWORD run = Morfit_3D_sequence_get_using_name(anim3d, "run");
		if(run==NULL)
			run = Morfit_3D_sequence_get_using_name(anim3d, "walk");
		
		DWORD stand = Morfit_3D_sequence_get_using_name(anim3d, "stand"); 

		gl_opponent.stand_sequence=stand;
		gl_opponent.run_sequence=run;

		if(run!=NULL) {
			Morfit_object_set_3D_sequence(gl_opponent.handle, run, 0);	
		}
		else if(stand!=NULL) {
			Morfit_object_set_3D_sequence(gl_opponent.handle, stand, 0);	
		}

		//Get the rest of the sequences
		gl_opponent.attack_sequence=Morfit_3D_sequence_get_using_name(anim3d, "attack");
		gl_opponent.pain_sequence=Morfit_3D_sequence_get_using_name(anim3d, "pain");
		gl_opponent.jump_sequence=Morfit_3D_sequence_get_using_name(anim3d, "jump");
		gl_opponent.flip_sequence=Morfit_3D_sequence_get_using_name(anim3d, "flip");
		gl_opponent.salute_sequence=Morfit_3D_sequence_get_using_name(anim3d, "salute");
		gl_opponent.taunt_sequence=Morfit_3D_sequence_get_using_name(anim3d, "taunt");
		gl_opponent.wave_sequence=Morfit_3D_sequence_get_using_name(anim3d, "wave");
		gl_opponent.point_sequence=Morfit_3D_sequence_get_using_name(anim3d, "point");
		gl_opponent.cdeath_sequence=Morfit_3D_sequence_get_using_name(anim3d, "cdeath");
		gl_opponent.death_sequence=Morfit_3D_sequence_get_using_name(anim3d, "death");
		//make the following sequences slower
		Morfit_3D_sequence_set_speed(gl_opponent.death_sequence, 4);
		Morfit_3D_sequence_set_speed(gl_opponent.cdeath_sequence, 4);
		
	}

	double x,y,z;
	Morfit_camera_get_location(camera, &x,&y,&z);
	Morfit_object_set_location( gl_opponent.handle, x+500,y,z);

	gl_opponent.stage=OPPONENT_IDLE;

	gl_opponent.speed=STEP*3; //Set the same speed as the player

	gl_opponent.got_hit_flag=NO;
	gl_opponent.number_of_times_got_hit=0;



	return(OK);

}


int initialize(double walking_height_effect[], DWORD sounds[])
{
	
	memset(&gl_player, 0, sizeof(player_data));
	memset(&gl_opponent, 0, sizeof(player_data));
	
	//Loads the world into the engine
	
	int rc=Morfit_engine_load_world("..\\Worlds\\arena\\arena.morfit","", "..\\Worlds\\arena\\bitmaps", USER_DEFINED_BEHAVIOR | MIPMAP_STABLE);
	if(rc==VR_ERROR) 
		return(VR_ERROR);

	

	//Disabling the cursor increases the engine speed.
	ShowCursor(FALSE);
	
	Morfit_engine_maximize_default_rendering_window();
	
	intitialize_walking_height_effect_array(walking_height_effect);

	DWORD camera=Morfit_camera_get_default_camera();

	//Lets load the md2 model
	if(load_md2_object("McLain", "McLain", camera)==VR_ERROR) return(VR_ERROR);
	
	if(load_weapon()==VR_ERROR) return(VR_ERROR);


	Morfit_camera_set_chase_type(camera,NO_CHASE);

	load_sounds(sounds);
	for (int i=0;i<100;i++)
		g_skyArray[i]=NULL;
	int sky_poly_count=0;
	for (DWORD poly=Morfit_polygon_get_first_polygon();poly!=NULL;poly=Morfit_polygon_get_next(poly))
	{
		if (!strcmp(Morfit_polygon_get_name(poly),"Sky")){
				
				g_skyArray[sky_poly_count]=poly;
				sky_poly_count++;
				
		}
		
	}
	

	gl_player.crosshair_pen=CreatePen(PS_SOLID, 1,RGB(140,140,100));

	//could be used if patches are not drawn cleanly
	//Morfit_engine_set_patches_offset(10);

	Morfit_3D_card_set_full_screen_mode(800,600);	

	GetClipCursor(&gl_player.window_rect);

	return(OK);

}




void set_weapon_direction(DWORD camera, double intersection[3], DWORD *blocking_polygon, DWORD *blocking_object)
{
	double location[3], weapon_location[3];

	set_vector(location, -VIEWER_SIZE/2, VIEWER_SIZE/4, -VIEWER_SIZE/4);
	Morfit_camera_convert_point_to_world_space(camera, location, weapon_location);

	Morfit_object_set_location(gl_player.weapon, weapon_location[0], weapon_location[1], weapon_location[2]);

	double camera_location[3]={0,0,0}; //the location of the camera in camera space
	double far_ahead[3]={-10000000, 0,0}; //A point in front of the camera (in camera space)

	//Morfit_camera_get_direction(camera, &direction[0], &direction[1], &direction[2]);
	*blocking_polygon=NULL;
	*blocking_object=NULL;
	double direction[3];
	int rc=Morfit_engine_is_movement_possible_camera_space(camera_location, far_ahead, blocking_polygon, intersection, blocking_object);
	if(rc==NO) {
		copy_vector(direction, intersection);
		sub_vector(direction, weapon_location);
	}
	else {
		//error there is no polygon in front of the camera
		Morfit_camera_get_direction(camera, &direction[0], &direction[1], &direction[2]);
	}
	
	double old_direction[3];
	//save the current direction
	Morfit_object_get_direction(gl_player.weapon, &old_direction[0], &old_direction[1], &old_direction[2]);
	
	mul_vector(direction, GUN_MOVING_SPEED);
	mul_vector(old_direction, (1-GUN_MOVING_SPEED));

	add_vector(direction, old_direction);

	Morfit_object_set_direction(gl_player.weapon, direction[0], direction[1], direction[2]);



}

void add_shooting_patch(double target[3], DWORD target_polygon, DWORD target_object)
{
	
	if(target_object!=NULL) {
		return; //So that we dont add patches on the shred and on the opponent
	}
	
	if(Morfit_polygon_get_patch_type(target_polygon)!=0) 
		return; //return if the target polygon is a patch in its self (to save in the numbers of polygons so that
				//we dont add one patch on top of the other)
	
	
	int translucent_type=Morfit_polygon_get_translucent(target_polygon);
	if(translucent_type!=DISABLE_BLENDING) 
		return; //We use translucent for the fire. We dont want to leave patched 
				//on the fire ...


	double direction[3]={0,0,1};
	int rgb[3];

	static double last_target[3]={0,0,0};

	//Check whether the current target is near the last one. If so we should
	//return to save polygons (otherwize we might have 30 patchs one on top of the other ...)
	//Note also that some cards dont draw overlapping patches cleanly
	//In this case setting the minimum distance to SHOOTING_MARK_RADIUS*2 will help
	double square_distance=Morfit_math_get_square_distance(last_target, target); //square_distance is faster than get_distance() ....
	if(square_distance<SHOOTING_MARK_RADIUS*SHOOTING_MARK_RADIUS*2) 
		return; //If the distance is less than a certain constant then we return
				//Note that if we did SHOOTING_MARK_RADIUS*SHOOTING_MARK_RADIUS*4 it would give exactly a distance of 2*SHOOTING_MARK_RADIUS
	
	DWORD patch=Morfit_polygon_add_patch_easy(target_polygon, target, SHOOTING_MARK_RADIUS, direction, gl_player.weapon_shooting_mark_bitmap, rgb);
	Morfit_polygon_set_translucent(patch, DARK_FILTER_GLASS);

	copy_vector(last_target, target);

}


//When we shoot at the wall, pieces of the wall fall down.
//Here we create such a piece and we make sure it falls down
void create_a_shooting_shred(double target[3], DWORD target_polygon, DWORD target_object)
{
	if(target_object!=NULL) 
		return; //Most likely that we hit a shred that we have created in the previous calls
	
	
	//create the shred
	//DWORD shred=Morfit_object_duplicate(gl_player.shooting_shred, NO); 
	//Note that calling Morfit_object_duplicate() with NO is faster but it will
	//make all the shreds have the same bitmap and color since when doing set_bitmap()
	//it is always the same polygon which is changed. Try and see ...
	DWORD shred=Morfit_object_duplicate(gl_player.shooting_shred, YES); 

	DWORD bmp=Morfit_polygon_get_bitmap_handle(target_polygon);
	if(bmp!=NULL) {
		DWORD all_polys[1000]; 
		Morfit_object_get_all_polygons(shred, all_polys); //actually there is only one polygon
		Morfit_polygon_set_bitmap_fill(all_polys[0], bmp);
	}
	
	//Set the location of the shred in front of the target polygon
	double normal[4];
	Morfit_polygon_get_plane(target_polygon, normal);
	mul_vector(normal, 5);
	add_vector(target, normal);
	Morfit_object_set_location(shred, target[0], target[1], target[2]);

	Morfit_object_set_chase_type(shred,CHASE_PHYSICS);
	
	Morfit_object_set_max_speed(shred, SHRED_SPEED*10); 
	
	double speed[3]={rand()-RAND_MAX/2,rand()-RAND_MAX/2,rand()};

	//Set the direction
	Morfit_object_set_speed(shred,speed);

	//Set the size of the speed
	Morfit_object_set_absolute_speed(shred,SHRED_SPEED);

	Morfit_object_set_friction(shred, 0);

	Morfit_object_set_elasticity(shred,0.4);

	double force[3]={0,0,-1};
	Morfit_object_set_force(shred,force);

	//Tell the engine to delete this object after 3 seconds
	Morfit_object_set_event(shred, 5000, MORFIT_DELETE);

}


void manage_shooting(DWORD sounds[], double target[3], DWORD target_polygon, DWORD target_object)
{
	
	int mouse_left_button= (GetAsyncKeyState(VK_LBUTTON)<0);
	if( (gl_player.is_shooting==NO) && mouse_left_button) {
		gl_player.is_shooting=YES;
		Morfit_sound_play(sounds[SOUND_SHOOT], SOUND_NO_LOOP);
		Morfit_polygon_enable(gl_player.fire_effect_polygon);
		Morfit_object_set_3D_sequence(gl_player.weapon, gl_player.weapon_shooting_sequence, 0);	
		return;
	
	}


	if( (gl_player.is_shooting==YES) && (mouse_left_button==0)) {
		//stop shooting
		gl_player.is_shooting=NO;
		Morfit_sound_stop(sounds[SOUND_SHOOT]);
		Morfit_polygon_disable(gl_player.fire_effect_polygon);
		Morfit_object_set_3D_sequence(gl_player.weapon, gl_player.weapon_idle_sequence, 0);	
		return;
	}


	if( gl_player.is_shooting==YES) {
		if(Morfit_sound_is_sound_playing(sounds[SOUND_SHOOT])==NO) {
			//stop shooting
			gl_player.is_shooting=NO;
			Morfit_polygon_disable(gl_player.fire_effect_polygon);
			Morfit_object_set_3D_sequence(gl_player.weapon, gl_player.weapon_idle_sequence, 0);	
			return;
		}

		//Now lets make a dark spot at the place where the weapon is aiming
		//and lets drop some stones on the ground
		add_shooting_patch(target, target_polygon, target_object);
		create_a_shooting_shred(target, target_polygon, target_object);

		if(target_object==gl_opponent.handle) {
			gl_opponent.got_hit_flag=YES;
		}

		
	}

}


void weapon_update(DWORD camera, DWORD sounds[])
{
	double target[3]; //the point that the weapon is aiming at
	DWORD  target_polygon, target_object;
	
	if(gl_player.weapon==NULL) return;
	
	set_weapon_direction(camera, target, &target_polygon, &target_object);

	manage_shooting(sounds, target, target_polygon, target_object);

}

void draw_crosshair(HDC dc_handle, DWORD camera) 
{
		int width=Morfit_camera_get_width(camera);
		int height=Morfit_camera_get_height(camera);

		//Get the center point
		int center_x=width/2;
		int center_y=height/2;

		//Draw a circle
		SelectObject(dc_handle, GetStockObject(GRAY_BRUSH)); 
		SelectObject(dc_handle, gl_player.crosshair_pen); 
		//SelectObject(dc_handle, gl_player.crosshair_brush); 
		Ellipse(dc_handle, center_x-3, center_y-3, center_x+3, center_y+3);
		Arc(dc_handle, center_x-12, center_y-12, center_x+12, center_y+12, center_x+12, center_y, center_x+12, center_y);

		/* //These lines could be added
		//The horizontal line
		MoveToEx(dc_handle, center_x-60, center_y, NULL);
		LineTo(dc_handle, center_x+60, center_y);
		//The vertical line
		MoveToEx(dc_handle, center_x, center_y-60, NULL);
		LineTo(dc_handle, center_x, center_y+60);
		*/
}



void opponent_move_idle(opponent_data *opponent)
{
	double location[3], direction[3], old_location[3], step[3];

	Morfit_object_get_location(opponent->handle, &location[0], &location[1], &location[2]);
	copy_vector(old_location, location); //save the original location for later use
	Morfit_object_get_direction(opponent->handle, &direction[0], &direction[1], &direction[2]);

	copy_vector(step, direction);
	mul_vector(step, opponent->speed);

	add_vector(location, step);

	//Normally it would have been enough to use opponent_move_with_collision_detection() , but
	//sometimes the opponent gets near a wall and then it slides along the wall slowly
	//In this case we will get did_move==YES but our opponent will look stupid ...
	//We add the following check to help in this situation
	double intersection[3];
	DWORD polygon, blocking_object;
	double body_size_addition[3];
	copy_vector(body_size_addition, direction);
	mul_vector(body_size_addition, opponent->opponent_width*1.5); //1.5 since we dont want to get too close ...
	
	add_vector(location, body_size_addition);
	int rc=Morfit_object_is_movement_possible(opponent->handle, old_location, location, &polygon, intersection, &blocking_object);
	sub_vector(location, body_size_addition);
	
	if(rc==YES) {
		Morfit_object_set_location(opponent->handle, location[0], location[1], location[2]);
		return;
	}

	//We got stuck so lets change direction
	//we choos a random direction
	int x,y;
	x=rand()-RAND_MAX/2;
	y=rand()-RAND_MAX/2;
	if( (x==0) && (y==0) ) x=1; //avoid the case of a direction==(0,0,0)
	set_vector(direction,x,y,0);
	
	Morfit_object_set_direction(opponent->handle, direction[0], direction[1], direction[2]);
}

void opponent_move_attack(opponent_data *opponent)
{


}

//In this function we change the sequence from time to time to make it
//more interesting
void opponent_idle_change_sequence(opponent_data *opponent)
{
	//A) Change sequence only when the player look at the opponent and
	//the opponent look at the player.
	DWORD camera=Morfit_camera_get_default_camera();
	double player_direction[3];
	Morfit_camera_get_direction(camera, &player_direction[0], &player_direction[1], &player_direction[2]);
	double opponent_direction[3];
	Morfit_object_get_direction(opponent->handle, &opponent_direction[0], &opponent_direction[1], &opponent_direction[2]);

	double cos_angle= -Morfit_math_product(opponent_direction, player_direction); 
	if(cos_angle<0) return; //return if there is no eye contact between the player and the opponent
							//That is if the angle between them is more than 90

	
	// B) If the opponent is close enough to the player then attack the player
	double opponent_location[3];
	Morfit_object_get_location(opponent->handle, &opponent_location[0], &opponent_location[1], &opponent_location[2]);
	
	double camera_location[3];
	Morfit_camera_get_location(camera, &camera_location[0], &camera_location[1], &camera_location[2]);	

	double distance=Morfit_math_get_distance(opponent_location, camera_location);

	//Calculate the direction from the opponent to the the player
	//The direction will be the location of the camera minus the location of the player
	double opponent_camera[3]; // a vector from the opponent to the camera
	copy_vector(opponent_camera, camera_location);
	sub_vector(opponent_camera, opponent_location);
	opponent_camera[2]=0;
	
	if(distance< OPPONENT_ATTACK_DISTANCE) {
		Morfit_object_set_3D_sequence(opponent->handle, opponent->attack_sequence, 0);	
		//Tell the engine that after finishing with the pain\death sequence it should set the running sequence
		Morfit_object_replace_3D_sequence_when_finished(opponent->handle, opponent->run_sequence,0 );
		Morfit_object_set_direction(opponent->handle, opponent_camera[0], opponent_camera[1], opponent_camera[2]);
		return;
	}

	// C) return if not currently running
	DWORD current_sequence=Morfit_object_get_3D_sequence(opponent->handle);
	if(current_sequence!=gl_opponent.run_sequence) return;

	
	//D) choose an interesting sequence

	int random_number=rand();
	if( (random_number % 20) !=0 ) return;

	//Get a number between 0 - 4
	random_number= random_number/(RAND_MAX/5);
	
	DWORD sequence;
	sequence=opponent->taunt_sequence;
	if(random_number==1)
		sequence=opponent->salute_sequence;
	else if(random_number==2)
		sequence=opponent->point_sequence;
	else if(random_number==3)
		sequence=opponent->wave_sequence;
	else if(random_number==4)
		sequence=opponent->stand_sequence;


	
	Morfit_object_set_3D_sequence(opponent->handle, sequence, 0);	
	//Tell the engine that after finishing with the pain\death sequence it should set the running sequence
	Morfit_object_replace_3D_sequence_when_finished(opponent->handle, opponent->run_sequence,0 );

	Morfit_object_set_direction(opponent->handle, opponent_camera[0], opponent_camera[1], opponent_camera[2]);


}

void opponent_move(opponent_data *opponent)
{
	DWORD current_sequence=Morfit_object_get_3D_sequence(opponent->handle);
	//if( (current_sequence!=gl_opponent.run_sequence) && (current_sequence!=opponent->attack_sequence)) return;
	if( current_sequence!=gl_opponent.run_sequence) return;
	
	
	if(opponent->stage==OPPONENT_IDLE) {
		opponent_move_idle(opponent);
		opponent_idle_change_sequence(opponent);
		return;
	}

	if(opponent->stage==OPPONENT_ATTACK) {
		opponent_move_attack(opponent);
		return;
	}

}

void opponent_check_if_was_hit(opponent_data *opponent)
{
	if(gl_opponent.got_hit_flag==NO) return;

	DWORD current_sequence=Morfit_object_get_3D_sequence(opponent->handle);
	if(current_sequence==gl_opponent.pain_sequence) return;
	if(current_sequence==gl_opponent.death_sequence) return;
	if(current_sequence==gl_opponent.cdeath_sequence) return;

	
	
	opponent->number_of_times_got_hit++;

	if( (opponent->number_of_times_got_hit %10) ==0) {
		//Every 3 hits we make him die temporarly
		Morfit_object_set_3D_sequence(opponent->handle, opponent->cdeath_sequence, 0);	
		//Tell the engine that after finishing with the pain\death sequence it should set the running sequence
		//Note the long transition period. It takes time to get back to life after dieing ...
		Morfit_object_replace_3D_sequence_when_finished(opponent->handle, opponent->run_sequence,0 );

	}
	else {
		Morfit_object_set_3D_sequence(opponent->handle, opponent->pain_sequence, 0);	
		//Tell the engine that after finishing with the pain\death sequence it should set the running sequence
		Morfit_object_replace_3D_sequence_when_finished(opponent->handle, opponent->run_sequence,0 );

	}
	

	gl_opponent.got_hit_flag=NO;


}

void opponent_update(opponent_data *opponent)
{
	opponent_manage_free_falling(opponent);

	opponent_move(opponent);

	opponent_check_if_was_hit(opponent);


}
void move_sky()
{
	int i=0;
	DWORD poly;
	while(1){
		poly=g_skyArray[i];
		if(poly==NULL)
			break;
		for (DWORD point=Morfit_polygon_get_first_point(poly);point!=NULL;point=Morfit_point_get_next_point(point))
		{
			double xy[2];
			Morfit_point_get_bitmap_xy(point,xy);
			xy[0]+=0.005;
			Morfit_point_set_bitmap_xy(point,poly,xy);
		}
		Morfit_polygon_is_valid(poly);
		i++;
	}
}
