Vehicle.cpp


#include "StdAfx.h"
#include "Vehicle.h"
#include "basecommandframelistener.h"

int Vehicle::mCount = 0;

Vector3 RandomVector3(float minx, float maxx, float miny, float maxy, float minz, float maxz) 
{
	Vector3 r;
	r.x = Math::RangeRandom(minx, maxx);
	r.y = Math::RangeRandom(miny, maxy);
	r.z = Math::RangeRandom(minz, maxz );
	return r;
}

Vehicle::Vehicle(BaseCommandFrameListener *frameListener, SceneManager *sceneMgr, VEHICLE_TYPE type, Vector3 offset, Vehicle *leader) : mSteeringBehavior(this)
{
	mType = type;
	mFrameListener = frameListener;
	mSceneMgr = sceneMgr;	

	mSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode( "planeNode" + StringConverter::toString(mCount) );
	if(mType == FIGHTER)
	{
		mEntity = mSceneMgr->createEntity( "plane"+ StringConverter::toString(mCount), "razor.mesh" );			
		mSceneNode->scale(0.5f, 0.5f, 0.5f);
	}
	else if(mType == BOMBER)
	{
		mEntity = mSceneMgr->createEntity( "plane"+ StringConverter::toString(mCount), "RZR-002.mesh" );	
		mSceneNode->scale(5.f, 5.f, 5.f);
	}	

	mSceneNode->attachObject(mEntity);
	mSceneNode->setPosition(mPosition);			
	mSceneNode->setFixedYawAxis(true);

	// Set initial properties
	mVelocity = Vector3::ZERO;	
	mMass = 1.0f;		
	mDead = false;

	if(offset != Vector3::ZERO)
	{
		mSteeringBehavior.offsetPursuitOn();
		mSteeringBehavior.setLeader(leader);			
		// Apply the same speed as the leader's
		mMaxSpeed = leader->maxSpeed();		
		mSteeringBehavior.addWaypoint(leader->position());
	}
	else
	{
		// Apply a random speed
		mMaxSpeed = Math::RangeRandom(600.0f, 1200.0f);
		mSteeringBehavior.followPathOn();		
		// Add waypoints		
		if(mType == FIGHTER)
		{
			mSteeringBehavior.addWaypoint(RandomVector3(-4000.f, 4000.f, 2500.f, 3000.f, -11000.f, -10500.f));
			mSteeringBehavior.addWaypoint(RandomVector3(-4000.f, 4000.f, 2500.f, 3000.f, -11000.f, -10500.f));
			mSteeringBehavior.addWaypoint(RandomVector3(-100.f, 1500.f, 250.f, 600.f, 400.f, 500.f));
			mSteeringBehavior.addWaypoint(RandomVector3(-100.f, 1500.f, 250.f, 600.f, 400.f, 500.f));
			mSteeringBehavior.addWaypoint(RandomVector3(-1500.f, 1000.f, 600.f, 1000.f, 1000.f, 1200.f));
			mSteeringBehavior.addWaypoint(RandomVector3(-1500.f, 1000.f, 600.f, 1000.f, 1000.f, 1200.f));
		}
		else if(mType == BOMBER)
		{
			// Bombers fly low
			mSteeringBehavior.addWaypoint(RandomVector3(-4000.f, 4000.f, 2500.f, 3000.f, -11000.f, -10500.f));
			mSteeringBehavior.addWaypoint(RandomVector3(-4000.f, 4000.f, 2500.f, 3000.f, -11000.f, -10500.f));
			mSteeringBehavior.addWaypoint(RandomVector3(-1500.f, 1500.f, 250.f, 400.f, 400.f, 500.f));
			mSteeringBehavior.addWaypoint(RandomVector3(-1500.f, 1500.f, 250.f, 400.f, 400.f, 500.f));
		}
	}
	// set current position to the first waypoint
	mPosition = mSteeringBehavior.currentDestionation();
	mOffset = offset;

	//LogManager::getSingleton().logMessage( "New plane created: " + StringConverter::toString(mPosition));
	mCount++;
	mFireTime = 0.f;
}


Vehicle::~Vehicle(void)
{
}

void Vehicle::update(float elapsedTime)
{
	updatePosition(elapsedTime);

	// update firing rate
	mFireTime += elapsedTime;

	Vector3 up = mSceneNode->_getDerivedOrientation() * Vector3::UNIT_Y;
	if(up.directionEquals(Vector3::UNIT_Y, Radian(Degree(45))))
	{
		if(mType == FIGHTER)
		{
			if(mFireTime > 1.0f)
			{
				// Only fire when facing player and close to the targets
				if(mForward.z >= 0.5f && mPosition.distance(Vector3(900.f, 0.0f, 1200.f)) < 2000.0f)
					fireMissile();			
				mFireTime = 0.0f;
			}
		}
		if(mType == BOMBER)
		{
			if(mFireTime > 0.4f)
			{
				// Bombers drop bomb even if not facing player
				if(mPosition.distance(Vector3(900.f, 0.0f, 1200.f)) < 2000.0f)
					dropBomb();
				mFireTime = 0.0f;
			}
		}
	}

	// update missiles
	std::list<Missile>::iterator list_iter;	
	for( list_iter = mMissiles.begin(); list_iter != mMissiles.end(); )
	{
		std::list<Missile>::iterator iter = list_iter;
		Missile *p = &*list_iter;
		p->update(elapsedTime);		
		*list_iter++;

		// See if missile hits the terrain
		if( p->mPosition.y <= gTerrainMgr->getTerrainInfo().getHeightAt( p->mPosition.x, p->mPosition.z ) )
		{	
			mFrameListener->createHole( elapsedTime, p->mPosition );				
			mSceneMgr->getRootSceneNode()->removeChild( p->mSceneNode ) ;												
			mMissiles.erase( iter );

		}	
	} 
}

void Vehicle::updatePosition(float elapsedTime)
{
	Vector3 steeringForce = mSteeringBehavior.calculate();

	mAcceleration = steeringForce / mMass;   

	mVelocity += mAcceleration * elapsedTime;

	// Clamp velocity
	if (mVelocity.length() > mMaxSpeed)
	{
		mVelocity.normalise();
		mVelocity *= mMaxSpeed;
	}	  

	// calculate forward and right vectors
	if(mVelocity.length() > 0.00000001)
	{
		mForward = mVelocity;
		mForward.normalise();
		mRight = mForward.crossProduct(Vector3::UNIT_Y);
	}

	mPosition += mVelocity * elapsedTime;
	mSceneNode->setPosition(mPosition);	

	// Set orientation
	Vector3 currentLookAt = mSceneNode->getOrientation( ) * Vector3::UNIT_Z;	

	Quaternion quat = Quaternion::ZERO ;
	quat = currentLookAt.getRotationTo( mForward) ;		

	Radian angle = Math::ACos((float)currentLookAt.dotProduct(mForward) );

	if((Degree(angle) > Degree(179)) )
	{
		mSceneNode->yaw(Degree(180));			
	}
	else
	{		
		mSceneNode->rotate(quat, Node::TS_PARENT);
	}
}


void Vehicle::clearMissiles()
{
	gLogMgr->logMessage( "clearing missiles....");
	std::list<Missile>::iterator list_iter;	
	for( list_iter = mMissiles.begin(); list_iter != mMissiles.end(); )
	{
		std::list<Missile>::iterator iter = list_iter;
		Missile *p = &*list_iter;
		*list_iter++;		
		mSceneMgr->getRootSceneNode()->removeChild( p->mSceneNode ) ;												
		mMissiles.erase( iter );			
		gLogMgr->logMessage( "missile removed");
	} 
}


void Vehicle::fireMissile()
{
	mCount++;

	Missile missile;

	missile.mPosition = mPosition;
	missile.mPosition.y -= 20.0f;

	missile.mEntity = mSceneMgr->createEntity( "missile" + StringConverter::toString(mCount), "Capsule.mesh" );			
	missile.mSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode( "missileNode" + StringConverter::toString(mCount) );
	missile.mSceneNode->attachObject( missile.mEntity );
	missile.mSceneNode->setPosition( missile.mPosition);		
	missile.mSceneNode->scale(4, 4, 4);

	missile.mVelocity = Vector3::ZERO;		
	missile.mMaxSpeed = 2500.0f;

	int spot = (int)Math::RangeRandom(0.0f, 1000.0f);

	// Random target, this really needs to be improved. Basically the desired behavior is the missile should fire
	// aligned a little bit below the plane and with the same velocity as of the plane.
	missile.mTarget = Vector3(Math::RangeRandom(0.0f, 1500.0f), 0.0f, Math::RangeRandom(0.0f, 1500.0f));
	Vector3 direction = mVelocity;
	direction.y = 0.f;
	direction.normalise();
	//missile->mTarget = mPosition + (direction * 1000.f);

	mMissiles.push_back(missile);
}

void Vehicle::dropBomb()
{
	mCount++;

	Missile missile;

	missile.mPosition = mPosition;
	missile.mPosition.y -= 20.0f;

	missile.mEntity = mSceneMgr->createEntity( "bomb" + StringConverter::toString(mCount), "Capsule.mesh" );			
	missile.mSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode( "bombNode" + StringConverter::toString(mCount) );	
	missile.mSceneNode->attachObject( missile.mEntity );
	missile.mSceneNode->setPosition( missile.mPosition);		
	missile.mSceneNode->scale(4, 4, 4);

	missile.mVelocity = Vector3::ZERO;		
	missile.mMaxSpeed = 600.0f;	

	missile.mTarget = Vector3(mPosition.x, mPosition.y - 10000.0f, mPosition.z);
	mMissiles.push_back(missile);
}

void Vehicle::kill()
{
	mPosition = Vector3(0.f, -10000.f, 0.f);
	mSceneNode->setVisible(false);
	mDead = true; 
}