/***************************************************************************
 * movingsprite.cpp  -  moving sprite class
 *
 * Copyright (C) 2003 - 2008 Florian Richter
 ***************************************************************************/
/*
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.
   
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "../core/globals.h"
#include "../objects/movingsprite.h"
#include "../core/main.h"
#include "../core/framerate.h"
#include "../core/game_core.h"
#include "../core/camera.h"
#include "../level/level.h"
#include "../user/preferences.h"
#include "../input/joystick.h"
#include "../audio/audio.h"
#include "../player/player.h"
#include "../enemies/turtle.h"
#include "../input/keyboard.h"
#include "../objects/box.h"
#include "../video/renderer.h"
#include "../video/gl_surface.h"
#include "../core/sprite_manager.h"

/* *** *** *** *** *** *** *** cMovingSprite *** *** *** *** *** *** *** *** *** *** */

cMovingSprite :: cMovingSprite( cGL_Surface *new_image /* = NULL */, float x /* = 0 */, float y /* = 0 */, bool del_img /* = 0 */ )
: cSprite( new_image, x, y, del_img )
{
	cMovingSprite::Init();
}

cMovingSprite :: cMovingSprite( CEGUI::XMLAttributes &attributes )
: cSprite()
{
	cMovingSprite::Init();
	cMovingSprite::Create_from_Stream( attributes );
}

cMovingSprite :: ~cMovingSprite( void )
{
	if( delete_image && image )
	{
		delete image;
	}

	Collisions_Clear();
}

void cMovingSprite :: Init( void )
{
	state = STA_STAY;

	velx = 0;
	vely = 0;
	
	direction = DIR_UNDEFINED;
	start_direction = DIR_UNDEFINED;
	can_be_on_ground = 1;
	ground_object = NULL;

	ice_resistance = 0;
	freeze_counter = 0;
}

cMovingSprite *cMovingSprite :: Copy( void )
{
	cMovingSprite *moving_sprite = new cMovingSprite( start_image, startposx, startposy );

	moving_sprite->type = type;
	moving_sprite->sprite_array = sprite_array;
	moving_sprite->Set_Massive_Type( massivetype );
	moving_sprite->can_be_ground = can_be_ground;
	moving_sprite->can_be_on_ground = can_be_on_ground;
	moving_sprite->Set_Ignore_Camera( no_camera );
	moving_sprite->Set_Shadow_Pos( shadow_pos );
	moving_sprite->Set_Shadow_Color( shadow_color );

	return moving_sprite;
}

void cMovingSprite :: Set_Image( cGL_Surface *new_image, bool new_start_image /* = 0 */, bool del_img /* = 0 */ )
{
	// get a possible collision point change
	GL_point col_pos_change = GL_point();

	if( image && new_image )
	{
		col_pos_change = new_image->col_pos - image->col_pos;
	}

	cSprite::Set_Image( new_image, new_start_image, del_img );

	// handle collision point change
	if( col_pos_change.x != 0 || col_pos_change.y != 0 )
	{
		Move( -col_pos_change.x, -col_pos_change.y, 1 );
	}

	// check onground
	Check_on_Ground();
}

void cMovingSprite :: Set_Direction( ObjectDirection dir, bool new_start_direction /* = 0 */ )
{
	direction = dir;

	// turn velocity if wrong
	if( ( dir == DIR_LEFT && velx > 0 ) || ( dir == DIR_RIGHT && velx < 0 ) )
	{
		velx *= -1;
	}

	if( new_start_direction )
	{
		start_direction = dir;
	}
}

void cMovingSprite :: Set_Direction( float angle, float speed, bool new_start_direction /* = 0 */ )
{
	velx = cos( angle * 0.01745329f ) * speed;
	vely = sin( angle * 0.01745329f ) * speed;

	Update_Direction();

	if( new_start_direction )
	{
		start_direction = direction;
	}
}

void cMovingSprite :: Auto_Slow_Down( float x_speed, float y_speed /* = 0 */ )
{
	// horizontal slow down
	if( x_speed > 0 )
	{
		if( velx > 0 )
		{
			Add_Velocity( -x_speed, 0 );

			if( velx < 0 )
			{
				velx = 0;
			}
		}
		else if( velx < 0 )
		{
			Add_Velocity( x_speed, 0 );

			if( velx > 0 )
			{
				velx = 0;
			}
		}
	}

	// vertical slow down
	if( y_speed > 0 )
	{
		if( vely > 0 )
		{
			Add_Velocity( 0, -y_speed );

			if( vely < 0 )
			{
				vely = 0;
			}
		}
		else if( vely < 0 )
		{
			Add_Velocity( 0, y_speed );

			if( vely > 0 )
			{
				vely = 0;
			}
		}
	}
}

void cMovingSprite :: Move( float move_x, float move_y, bool real /* = 0 */ )
{
	cSprite::Move( move_x, move_y, real );

	// check/handle if moved out of level rect
	Check_out_of_Level_Hor( move_x, 1 );
	Check_out_of_Level_Ver( move_y, 1 );
}

ObjectDirection cMovingSprite :: Col_Move( float move_x, float move_y, bool real /* = 0 */, bool force /* = 0 */, bool check_on_ground /* = 1 */ )
{
	// nothing to move
	if( move_x == 0 && move_y == 0 )
	{
		return DIR_UNDEFINED;
	}

	// invalid collision rect
	if( col_rect.w == 0 || col_rect.h == 0 )
	{
		return DIR_UNDEFINED;
	}

	// use speedfactor
	if( !real )
	{
		move_x *= pFramerate->speedfactor;
		move_y *= pFramerate->speedfactor;
	}

	ObjectDirection collision_dir = DIR_UNDEFINED;
 
	// check for collisions
	if( !force )
	{
		float posx_old = posx;
		float posy_old = posy;
		float posx_final = posx + move_x;
		float posy_final = posy + move_y;
		float check_x = move_x;
		float check_y = move_y;

		// check if object collision rect is smaller as the position check size
		if( check_x > col_rect.w )
		{
			check_x = col_rect.w;
		}
		else if( check_x < -col_rect.w )
		{
			check_x = -col_rect.w;
		}

		if( check_y > col_rect.h )
		{
			check_y = col_rect.h;
		}
		else if( check_y < -col_rect.h )
		{
			check_y = -col_rect.h;
		}

		// collision list if collisions found
		cObjectCollisionType col_list;

		/* Checks in both directions simultaneous using collision rects
		 * if a collision occurs moves to pixel collision testing
		*/
		while( check_x < -0.01f || check_x > 0.01f || check_y < -0.01f || check_y > 0.01f )
		{
			col_list = Collision_Check_Relative( check_x, check_y, 0, 0, COLLIDE_ALLOW_INTERNAL );

			if( !col_list.size() )
			{
				// update horizontal
				if( check_x != 0 )
				{
					posx += check_x;

					if( ( check_x > 0 && posx_final <= posx ) || ( check_x < 0 && posx_final >= posx ) )
					{
						posx = posx_final;
						check_x = 0;
					}
				}

				// update vertical
				if( check_y != 0 )
				{
					posy += check_y;

					if( ( check_y > 0 && posy_final <= posy ) || ( check_y < 0 && posy_final >= posy ) )
					{
						posy = posy_final;
						check_y = 0;
					}
				}

				// update collision rects
				Update_Position_Rect();
			}
			else
			{
				break;
			}
		}

		// if a collision is found enter pixel checking
		if( col_list.size() )
		{
			// change to pixel checking
			if( check_x < -1 )
			{
				check_x = -1;
			}
			else if( check_x > 1 )
			{
				check_x = 1;
			}

			if( check_y < -1 )
			{
				check_y = -1;
			}
			else if( check_y > 1 )
			{
				check_y = 1;
			}

			// collision list link for checking
			SpriteList *sprite_list = &col_list.list;

			// collision list if collisions found in pixel checking
			cObjectCollisionType col_list_pixel;

			/* Checks in both directions simultaneous
			 * if a collision occurs it saves the direction
			*/
			while( check_x < -0.01f || check_x > 0.01f || check_y < -0.01f || check_y > 0.01f )
			{
				if( check_x != 0 )
				{
					// collision check
					col_list_pixel = Collision_Check_Relative( check_x, 0, 0, 0, COLLIDE_COMPLETE, sprite_list );

					if( !col_list_pixel.size() )
					{
						posx += check_x;

						if( ( check_x > 0 && posx_final <= posx ) || ( check_x < 0 && posx_final >= posx ) )
						{
							posx = posx_final;
							check_x = 0;
						}

						// update collision rects
						Update_Position_Rect();
					}
					// collision found
					else
					{
						if( collision_dir == DIR_UNDEFINED )
						{
							// Collision Left/Right
							collision_dir = DIR_HORIZONTAL;
						}
						else
						{
							// Collision Up/Down/Left/Right
							collision_dir = DIR_ALL;
						}
						
						check_x = 0;
					}
				}

				if( check_y != 0 )
				{
					// collision check
					col_list_pixel = Collision_Check_Relative( 0, check_y, 0, 0, COLLIDE_COMPLETE, sprite_list );

					if( !col_list_pixel.size() )
					{
						posy += check_y;

						if( ( check_y > 0 && posy_final <= posy ) || ( check_y < 0 && posy_final >= posy ) )
						{
							posy = posy_final;
							check_y = 0;
						}

						// update collision rects
						Update_Position_Rect();
					}
					// collision found
					else
					{
						if( collision_dir == DIR_UNDEFINED ) 
						{
							// Collision Up/Down
							collision_dir = DIR_VERTICAL;
						}
						else
						{
							// Collision Up/Down/Left/Right
							collision_dir = DIR_ALL;
						}

						check_y = 0;
					}
				}
			}
		}
	}
	// don't check for collisions
	else
	{
		posx += move_x;
		posy += move_y;
	}

	if( collision_dir == DIR_UNDEFINED )
	{
		// update the position values
		Update_Position_Rect();
	}

	// if check on ground
	if( check_on_ground )
	{
		Check_on_Ground();
	}

	// check/handle if moved out of level rect
	Check_out_of_Level_Hor( move_x, 1 );
	Check_out_of_Level_Ver( move_y, 1 );

	return collision_dir;
}

void cMovingSprite :: Set_Velocity( float x, float y )
{
	velx = x;
	vely = y;
}

void cMovingSprite :: Add_Velocity( float x, float y, bool real /* = 0 */ )
{
	if( real )
	{
		velx += x;
		vely += y;
	}
	else
	{
		velx += x * pFramerate->speedfactor;
		vely += y * pFramerate->speedfactor;
	}
}

void cMovingSprite :: Turn_Around( ObjectDirection col_dir /* = DIR_UNDEFINED */ )
{
	if( col_dir != DIR_UNDEFINED )
	{
		// check if the collision direction is in front
		if( ( col_dir == DIR_RIGHT && direction != col_dir ) || ( col_dir == DIR_LEFT && direction != col_dir ) ||
			( col_dir == DIR_UP && direction != col_dir ) || ( col_dir == DIR_DOWN && direction != col_dir ) )
		{
			// collision direction is not in front
			return;
		}
	}

	// reverse velocity
	if( direction == DIR_LEFT || direction == DIR_RIGHT )
	{
		velx *= -1;
	}
	else if( direction == DIR_UP || direction == DIR_DOWN )
	{
		vely *= -1;
	}

	Update_Direction();
}

void cMovingSprite :: Update( void )
{
	if( freeze_counter > 0 )
	{
		freeze_counter -= pFramerate->speedfactor;

		if( freeze_counter <= 0 )
		{
			freeze_counter = 0;
			Update_Valid_Update();
		}
	}
}

void cMovingSprite :: Draw( cSurfaceRequest *request /* = NULL */ )
{
	if( !valid_draw )
	{
		return;
	}

	bool create_request = 0;

	if( !request )
	{
		create_request = 1;
		// create request
		request = new cSurfaceRequest();
	}

	cSprite::Draw( request );

	if( !editor_enabled )
	{
		// frozen
		if( freeze_counter )
		{
			request->combine_type = GL_ADD;

			float counter_add = freeze_counter;

			if( counter_add > 1000 )
			{
				counter_add = 1000;
			}

			request->combine_col[0] = counter_add * 0.003f;
			request->combine_col[1] = counter_add * 0.003f;
			request->combine_col[2] = counter_add * 0.0099f;
		}
	}

	if( create_request )
	{
		// add request
		pRenderer->Add( request );
	}
}

cObjectCollisionType cMovingSprite :: Collision_Check_Relative( const float x, const float y, const float w /* = 0 */, const float h /* = 0 */, const ColCheckType check_type /* = COLLIDE_COMPLETE */, SpriteList *objects /* = NULL */ )
{
	return Collision_Check_Absolute( col_rect.x + x, col_rect.y + y, w, h, check_type, objects );
}

cObjectCollisionType cMovingSprite :: Collision_Check_Absolute( const float x, const float y, const float w /* = 0 */, const float h /* = 0 */, const ColCheckType check_type /* = COLLIDE_COMPLETE */, SpriteList *objects /* = NULL */ )
{
	// save original rect
	GL_rect new_rect;

	// if given use x position
	if( x != 0 )
	{
		new_rect.x = x;
	}
	else
	{
		new_rect.x = col_rect.x;
	}

	// if given use y position
	if( y != 0 )
	{
		new_rect.y = y;
	}
	else
	{
		new_rect.y = col_rect.y;
	}

	// if given use width
	if( w > 0 )
	{
		new_rect.w = w;
	}
	else
	{
		new_rect.w = col_rect.w;
	}

	// if given use height
	if( h > 0 )
	{
		new_rect.h = h;
	}
	else
	{
		new_rect.h = col_rect.h;
	}

	// visual debugging
	if( game_debug )
	{
		// create request
		cRectRequest *request = new cRectRequest();

		pVideo->Draw_Rect( &new_rect, posz + 0.00001f, &green, request );
		request->no_camera = 0;

		request->blend_sfactor = GL_SRC_COLOR;
		request->blend_dfactor = GL_DST_ALPHA;


		// add request
		pRenderer->Add( request );
	}

	// return collisions list
	return Collision_Check( &new_rect, check_type, objects );
}
	
cObjectCollisionType cMovingSprite :: Collision_Check( const GL_rect *new_rect, const ColCheckType check_type /* = COLLIDE_COMPLETE */, SpriteList *objects /* = NULL */ )
{
	// blocking collisions list
	cObjectCollisionType col_list = cObjectCollisionType();

	// no width or height is invalid
	if( new_rect->w == 0 || new_rect->h == 0 )
	{
		return col_list;
	}

	// if no object list is given get all objects available
	if( !objects )
	{
		objects = &pActive_Sprite_Manager->objects;
	}

	// Check Objects
	for( SpriteList::iterator itr = objects->begin(), itr_end = objects->end(); itr != itr_end; ++itr )
	{
		// get object pointer
		cSprite *level_object = (*itr);

		// if the same object or destroyed object
		if( this == level_object || level_object->destroy )
		{
			continue;
		}

		// if not Active/Massive/Enemy
		if( !( level_object->sprite_array == ARRAY_ACTIVE || level_object->sprite_array == ARRAY_MASSIVE || level_object->sprite_array == ARRAY_ENEMY ) )
		{
			continue;
		}

		// if rects doesn't touch
		if( !Col_Box( new_rect, &level_object->col_rect ) )
		{
			continue;
		}

		// if enemy is dead
		if( level_object->sprite_array == ARRAY_ENEMY && static_cast<cEnemy *>(level_object)->dead )
		{
			continue;
		}

		// if col_valid is 1 it's an internal collision
		unsigned int col_valid = Validate_Collision( level_object );

		// not a valid collision
		if( !col_valid )
		{
			continue;
		}

		// ignore internal collisions
		if( check_type == COLLIDE_ALLOW_BLOCKING && col_valid == 1 )
		{
			continue;
		}

		// todo : check if check_type should be used instead for validation
		// add to list if full collision
		if( col_valid == 2 )
		{
			col_list.add( level_object );
		}

		// ignore full collisions
		if( check_type == COLLIDE_ALLOW_INTERNAL && col_valid == 2 )
		{
			continue;
		}

		// if only checking
		if( check_type == COLLIDE_ONLY_CHECK )
		{
			continue;
		}

		// add collision
		Collision_Add( this, level_object, pActive_Sprite_Manager->Get_Array_Num( level_object ), Get_Collision_Type( level_object->sprite_array ), ( col_valid == 1 ) ? (1) : (0) );
	}

	// Player
	if( type != TYPE_PLAYER && Col_Box( new_rect, &pActive_Player->col_rect ) )
	{
		// if col_valid is 1 it's an internal collision
		unsigned int col_valid = Validate_Collision( pActive_Player );

		// ignore internal collisions
		if( check_type == COLLIDE_ALLOW_BLOCKING && col_valid == 1 )
		{
			col_valid = 0;
		}

		if( col_valid )
		{
			// full collision
			if( col_valid == 2 )
			{
				col_list.add( pActive_Player );
			}

			// ignore full collisions
			if( !( check_type == COLLIDE_ALLOW_INTERNAL && col_valid == 2 ) )
			{
				if( check_type != COLLIDE_ONLY_CHECK )
				{
					Collision_Add( this, pActive_Player, 0, CO_PLAYER, ( col_valid == 1 ) ? (1) : (0) );
				}
			}
		}
	}

	return col_list;
}

bool cMovingSprite :: Check_out_of_Level_Hor( const float move_x, const bool handle /* = 0 */ )
{
	// left
	if( col_rect.x < pActive_Camera->limit_rect.x && col_rect.x - ( move_x - 0.00001f ) >= pActive_Camera->limit_rect.x  )
	{
		if( handle )
		{
			Handle_out_of_Level( DIR_LEFT );
		}

		return 1;
	}
	// right
	else if( col_rect.x + col_rect.w > pActive_Camera->limit_rect.x + pActive_Camera->limit_rect.w && col_rect.x + col_rect.w - ( move_x + 0.00001f ) <= pActive_Camera->limit_rect.x + pActive_Camera->limit_rect.w )
	{
		if( handle )
		{
			Handle_out_of_Level( DIR_RIGHT );
		}

		return 1;
	}

	return 0;
}

bool cMovingSprite :: Check_out_of_Level_Ver( const float move_y, const bool handle /* = 0 */ )
{
	// top
	if( col_rect.y < pActive_Camera->limit_rect.y + pActive_Camera->limit_rect.h && col_rect.y - ( move_y - 0.00001f ) >= pActive_Camera->limit_rect.h + pActive_Camera->limit_rect.y )
	{
		if( handle )
		{
			Handle_out_of_Level( DIR_TOP );
		}

		return 1;
	}
	// bottom
	else if( col_rect.y + col_rect.h > pActive_Camera->limit_rect.y + game_res_h && col_rect.y + col_rect.h - ( move_y + 0.00001f ) <= pActive_Camera->limit_rect.y + game_res_h  )
	{
		if( handle )
		{
			Handle_out_of_Level( DIR_BOTTOM );
		}

		return 1;
	}

	return 0;
}

void cMovingSprite :: Set_on_Ground( cSprite *obj, bool set_on_top /* = 1 */ )
{
	// invalid or can't be on ground
	if( !obj || !can_be_on_ground )
	{
		return;
	}

	// if wanted object can't be ground object
	if( !obj->can_be_ground )
	{
		return;
	}

	// set groundobject
	ground_object = obj;
	// set on top
	if( set_on_top )
	{
		Set_on_Top( ground_object, 0 );
	}
}

void cMovingSprite :: Check_on_Ground( void )
{
	// can't be on ground
	if( !can_be_on_ground )
	{
		return;
	}

	if( type != TYPE_PLAYER && sprite_array != ARRAY_ENEMY && sprite_array != ARRAY_ACTIVE )
	{
		return;
	}

	// if ground object
	if( ground_object )
	{
		GL_rect rect2( col_rect.x, col_rect.y + col_rect.h, col_rect.w, 1 );

		// if on ground object
		if( Col_Box( &ground_object->col_rect, &rect2 ) && ground_object->can_be_ground )
		{
			return;
		}
	}

	// don't check if flying or linked
	if( state == STA_FLY || state == STA_OBJ_LINKED )
	{
		return;
	}

	// new onground check
	cObjectCollisionType col_objects = Collision_Check_Relative( 0, col_rect.h, 0, 1, COLLIDE_ALLOW_BLOCKING );

	Reset_on_Ground();

	// possible ground objects
	for( SpriteList::iterator itr = col_objects.list.begin(), itr_end = col_objects.list.end(); itr != itr_end; ++itr )
	{
		cObjectCollision *collision = Collision_Get_last();

		// no valid collision
		if( !collision )
		{
			continue;
		}

		// ground collision found
		if( collision->direction == DIR_BOTTOM )
		{
			Set_on_Ground( *itr );
			// send collision ( for falling platform )
			Send_Collision( collision );
		}
		
		Collision_Delete( collision );
	}
}

void cMovingSprite :: Reset_on_Ground( void )
{
	ground_object = NULL;
}

void cMovingSprite :: Update_Anti_Stuck( void )
{
	// collision count
	unsigned int count = Collision_Check( &col_rect, COLLIDE_ALLOW_BLOCKING ).size();

	// check collisions
	for( unsigned int i = 0; i < count; i++ )
	{
		cObjectCollision *collision = Collision_Get_last();

		if( !collision )
		{
			continue;
		}

		cSprite *col_obj = pActive_Sprite_Manager->objects[collision->number];

		if( collision->type != CO_ENEMY && !( collision->type == CO_ACTIVE && ( col_obj->massivetype == MASS_HALFMASSIVE || col_obj->massivetype == MASS_CLIMBABLE ) ) )
		{
			debug_print( "Anti Stuck detected object %s on %s side\n", col_obj->name.c_str(), Get_Direction_Name( collision->direction ).c_str() );

			if( collision->direction == DIR_LEFT ) 
			{
				Col_Move( 1, 0, 0, 1 );
			}
			else if( collision->direction == DIR_RIGHT ) 
			{
				Col_Move( -1, 0, 0, 1 );
			}
			else if( collision->direction == DIR_UP ) 
			{
				Col_Move( 0, 1, 0, 1 );
			}
			else if( collision->direction == DIR_DOWN ) 
			{
				Col_Move( 0, -1, 0, 1 );
			}
		}

		Collision_Delete( collision );
	}
}

void cMovingSprite :: Collide_Move( void )
{
	if( !valid_update || !is_Player_range() )
	{
		return;
	}

	// move and create collision data
	Col_Move( velx, vely );

	// if the ground object moves also move this sprite
	if( ground_object && ( ground_object->sprite_array == ARRAY_ACTIVE || ground_object->sprite_array == ARRAY_ENEMY ) ) // || ground_object->sprite_array == ARRAY_MASSIVE
	{
		cMovingSprite *moving_ground_object = dynamic_cast<cMovingSprite *>(ground_object);

		// if valid moving sprite
		if( moving_ground_object )
		{
			if( moving_ground_object->velx != 0 || moving_ground_object->vely != 0 )
			{
				// check ground first because of the moving object velocity
				Check_on_Ground();
				// save posx for possible can not move test
				float posy_orig = posy;
				/* stop object from getting stopped of the moving object which did not yet move itself
				 * for example the player always moves as last
				*/
				bool is_massive = 0;
				if( moving_ground_object->massivetype == MASS_MASSIVE )
				{
					moving_ground_object->massivetype = MASS_PASSIVE;
					is_massive = 1;
				}
				// move
				Col_Move( moving_ground_object->velx, moving_ground_object->vely, 0, 0, 0 );

				if( is_massive )
				{
					moving_ground_object->massivetype = MASS_MASSIVE;
				}
				// if ground object is moving up
				if( moving_ground_object->vely < 0 )
				{
					// test if we could not move upwards because something did block us in Col_Move()
					if( posy == posy_orig )
					{
						// massive
						if( moving_ground_object->massivetype == MASS_MASSIVE )
						{
							// got crunched
							DownGrade( 1 );
						}
						// halfmassive
						else if( moving_ground_object->massivetype == MASS_HALFMASSIVE )
						{
							// lost ground
							Move( 0, 1.9f, 1 );
							Reset_on_Ground();
						}
					}
				}
			}
		}
	}
}

void cMovingSprite :: Freeze( float freeze_time /* = DESIRED_FPS * 10 */ )
{
	freeze_counter = freeze_time;

	// apply resistance
	if( ice_resistance > 0 )
	{
		freeze_counter *= ( ice_resistance * -1 ) + 1;
	}

	Update_Valid_Update();
}

void cMovingSprite :: DownGrade( bool force )
{
	// virtual
}

void cMovingSprite :: Update_Direction( void )
{
	if( velx < 0 )
	{
		direction = DIR_LEFT;
	}
	else if( velx > 0 )
	{
		direction = DIR_RIGHT;
	}
	else if( vely < 0 )
	{
		direction = DIR_UP;
	}
	else if( vely > 0 )
	{
		direction = DIR_DOWN;
	}
}

void cMovingSprite :: Update_Rotation_Hor_velx( bool start_rotation /* = 0 */ )
{
	// left
	if( velx < 0 )
	{
		roty = 0;

		if( start_rotation )
		{
			start_roty = roty;
		}
	}
	// right
	else if( velx > 0 )
	{
		roty = 180;

		if( start_rotation )
		{
			start_roty = roty;
		}
	}
}

void cMovingSprite :: Update_Rotation_Hor_vely( bool start_rotation /* = 0 */ )
{
	// up
	if( vely < 0 )
	{
		roty = 0;

		if( start_rotation )
		{
			start_roty = roty;
		}
	}
	// down
	else if( vely > 0 )
	{
		roty = 180;

		if( start_rotation )
		{
			start_roty = roty;
		}
	}
}

void cMovingSprite :: Handle_Move_Object_Collision( cObjectCollision *collision )
{
	// if not massive
	if( massivetype != MASS_MASSIVE )
	{
		return;
	}

	// get object
	cMovingSprite *obj = NULL;

	if( collision->type == CO_ENEMY )
	{
		obj = static_cast<cMovingSprite *>(pActive_Sprite_Manager->Get_Pointer( collision->number ));

		// ignore these enemies
		if( obj->type == TYPE_THROMP || obj->type == TYPE_EATO || obj->type == TYPE_JPIRANHA || obj->type == TYPE_STATIC_ENEMY )
		{
			return;
		}
	}
	else if( collision->type == CO_PLAYER )
	{
		obj = static_cast<cMovingSprite *>(pPlayer);
	}
	// not a valid type
	else
	{
		return;
	}

	// top collision is handled in cMovingSprite::Collide_Move()
	if( collision->direction == DIR_BOTTOM )
	{
		if( obj->ground_object && obj->ground_object->massivetype == MASS_MASSIVE )
		{
			// got crunched
			obj->DownGrade( 1 );
		}
		else
		{
			// move
			obj->Col_Move( 0, vely, 0, 0, 0 );
		}
	}
	else if( ( collision->direction == DIR_LEFT && velx < 0 ) || ( collision->direction == DIR_RIGHT && velx > 0 ) )
	{
		// save posx for possible can not move test
		float posx_orig = obj->posx;
		// move
		obj->Col_Move( velx, 0, 0, 0, 0 );

		// test if we could not move it upwards because something did block it
		if( obj->posx == posx_orig )
		{
			// got crunched
			obj->DownGrade( 1 );
		}
	}
}

unsigned int cMovingSprite :: Validate_Collision( cSprite *obj )
{
	if( obj->massivetype == MASS_MASSIVE )
	{
		return 2;
	}
	if( obj->massivetype == MASS_HALFMASSIVE )
	{
		// if moving downwards and the object is on bottom
		if( vely >= 0 && Is_on_Top( obj ) )
		{
			return 2;
		}
	}

	return 0;
}

int cMovingSprite :: Validate_Collision_Ghost( cSprite *obj )
{
	if( obj->type == TYPE_BONUSBOX || obj->type == TYPE_SPINBOX )
	{
		cBaseBox *box = static_cast<cBaseBox *>(obj);

		// ghost
		if( box->box_invisible == BOX_GHOST )
		{
			// maryo is not ghost
			if( pPlayer->maryo_type != MARYO_GHOST )
			{
				return 0;
			}
		}
	}

	return -1;
}

int cMovingSprite :: Validate_Collision_Object_On_Top( cMovingSprite *moving_sprite )
{
	// don't handle if not moving upwards or slower
	if( moving_sprite->Is_on_Top( this ) && direction == DIR_UP && moving_sprite->vely > vely && moving_sprite->can_be_on_ground )
	{
		// halfmassive
		if( massivetype == MASS_HALFMASSIVE )
		{
			// only if no ground
			if( !moving_sprite->ground_object )
			{
				return 2;
			}
		}
		// massive
		else if( massivetype == MASS_MASSIVE )
		{
			// always pick up
			if( moving_sprite->ground_object != this )
			{
				moving_sprite->ground_object = this;
				return 0;
			}
		}
	}

	return -1;
}

void cMovingSprite :: Send_Collision( cObjectCollision *collision )
{
	// empty collision
	if( !collision )
	{
		return;
	}

	// if no target object number is available
	if( collision->number < 0 )
	{
		return;
	}

	/* if collision is received ignore it
	 * a received collision can't create another received collision
	 * only a self detected collision can create a received collision
	*/
	if( collision->received )
	{
		return;
	}

	// create a new collision
	cObjectCollision *ncollision = new cObjectCollision();

	// this is a received collision
	ncollision->received = 1;
	// set object manager id
	if( type != TYPE_PLAYER )
	{
		ncollision->number = pActive_Sprite_Manager->Get_Array_Num( this );

		// object not available
		if( ncollision->number < 0 )
		{
			debug_print( "Warning : Object did send Collision but doesn't exists in Manager\n" );

			delete ncollision;
			return;
		}
	}

	// set direction
	if( collision->direction != DIR_UNDEFINED )
	{
		ncollision->direction = Get_Opposite_Direction( collision->direction );
	}

	// set type
	if( type != TYPE_PLAYER )
	{
		ncollision->type = Get_Collision_Type( sprite_array );
	}
	// from player
	else
	{
		ncollision->type = CO_PLAYER;
	}

	// add collision to the list
	if( collision->type == CO_PLAYER )
	{
		pActive_Player->Collision_Add( ncollision, 1 );
	}
	else
	{
		pActive_Sprite_Manager->Get_Pointer( collision->number )->Collision_Add( ncollision, 1 );
	}
}

void cMovingSprite :: Handle_Collision( cObjectCollision *collision )
{
	// ignore player/enemy if frozen
	if( collision->type == CO_PLAYER || collision->type == CO_ENEMY )
	{
		if( freeze_counter )
		{
			return;
		}
	}

	cSprite::Handle_Collision( collision );
}

void cMovingSprite :: Handle_out_of_Level( ObjectDirection dir )
{
	// virtual
}

/* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
