//===========================================================================//
// File:        mover.cc                                                            //
// Project: MUNGA           Brick: Model Manager                             //
// Contents: Implementation details of moving entity class                   //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 01/21/95  JMA  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc.                     //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include <munga.hpp>
#pragma hdrstop

#if !defined(MOVER_HPP)
	#include <mover.hpp>
#endif

#if !defined(PLAYER_HPP)
	#include <player.hpp>
#endif

#if !defined(BOXSOLID_HPP)
	#include <boxsolid.hpp>
#endif

#if !defined(INTEREST_HPP)
	#include <interest.hpp>
#endif

#if !defined(COLLASST_HPP)
	#include <collasst.hpp>
#endif

#if !defined(DOORFRAM_HPP)
	#include <doorfram.hpp>
#endif

#if !defined(DOOR_HPP)
	#include <door.hpp>
#endif

#if !defined(LINE_HPP)
	#include <line.hpp>
#endif

#if !defined(APP_HPP)
	#include <app.hpp>
#endif

#if !defined(NOTATION_HPP)
	#include <notation.hpp>
#endif

//#############################################################################
//###############################    Mover    #################################
//#############################################################################

//#############################################################################
// Shared Data Support
//
Derivation
	Mover::ClassDerivations(
	Entity::ClassDerivations,
      "Mover"
   );

Mover::SharedData
	Mover::DefaultData(
		Mover::ClassDerivations,
		Mover::MessageHandlers,
		Mover::AttributeIndex,
		Mover::StateCount,
		(Entity::MakeHandler)Mover::Make
	);

//#############################################################################
// Message Support
//
#if 0
const Receiver::HandlerEntry
	Mover::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(Mover, Update)
};

Entity::MessageHandlerSet
	Mover::MessageHandlers(
		ELEMENTS(Mover::MessageHandlerEntries),
		Mover::MessageHandlerEntries,
		Entity::MessageHandlers
	);
#endif

//#############################################################################
// Attribute Support
//
const Mover::IndexEntry
	Mover::AttributePointers[]=
{
	ATTRIBUTE_ENTRY(Mover, LocalVelocity,                           localVelocity),
	ATTRIBUTE_ENTRY(Mover, LocalAcceleration,               localAcceleration),
	ATTRIBUTE_ENTRY(Mover, WorldLinearVelocity,             worldLinearVelocity),
	ATTRIBUTE_ENTRY(Mover, WorldLinearAcceleration, worldLinearAcceleration),
	ATTRIBUTE_ENTRY(Mover, MoverMass,                                       moverMass),
	ATTRIBUTE_ENTRY(Mover, MomentOfInertia,                 momentOfInertia),
	ATTRIBUTE_ENTRY(
		Mover,
		PositiveLinearDragCoefficients,
		positiveLinearDragCoefficients
	),
	ATTRIBUTE_ENTRY(
		Mover,
		NegativeLinearDragCoefficients,
		negativeLinearDragCoefficients
	),
	ATTRIBUTE_ENTRY(Mover, AngularDragCoefficients, angularDragCoefficients),
	ATTRIBUTE_ENTRY(Mover, FrictionCoefficient,             frictionCoefficient),
	ATTRIBUTE_ENTRY(Mover, ElasticityCoefficient,   elasticityCoefficient),
	ATTRIBUTE_ENTRY(Mover, MinimumBounceSpeed,              minimumBounceSpeed)
};

Mover::AttributeIndexSet
	Mover::AttributeIndex(
		ELEMENTS(Mover::AttributePointers),
		Mover::AttributePointers,
		Entity::AttributeIndex
	);

//#############################################################################
// Model Support
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::UpdateWorldMotion()
{
	Check(this);

	//
	//---------------------------------------------------
	// Move the accelerations back into world coordinates
	//---------------------------------------------------
	//
	worldLinearAcceleration.Multiply(
		localAcceleration.linearMotion,
		localToWorld
	);
	worldLinearVelocity.Multiply(
		localVelocity.linearMotion,
		localToWorld
	);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::UpdateLocalMotion()
{
	Check(this);
	localVelocity.linearMotion.MultiplyByInverse(
		worldLinearVelocity,
		localToWorld
	);
	localAcceleration.linearMotion.MultiplyByInverse(
		worldLinearAcceleration,
		localToWorld
	);
   Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::ApplyWorldAccelerations(Scalar time_slice)
{
	Check(this);
	Verify(time_slice > 0.0f);

	//
	//--------------------------------------------------
	// Calculate the new position as p += v*t + a*.5*t*t
	//--------------------------------------------------
	//
	Scalar
		half_t_squared = 0.5f * time_slice * time_slice;
	Vector3D
		position_delta;
	position_delta.Multiply(worldLinearAcceleration, half_t_squared);
	Check_Fpu();

	position_delta.AddScaled(
		position_delta,
		worldLinearVelocity,
		time_slice
	);
	Check_Fpu();

	localOrigin.linearPosition.Add(localOrigin.linearPosition, position_delta);
	Check_Fpu();

	position_delta.Multiply(localAcceleration.angularMotion, half_t_squared);
	Check_Fpu();

	position_delta.AddScaled(
		position_delta,
		localVelocity.angularMotion,
		time_slice
	);
	Check_Fpu();

	Quaternion
		old_position = localOrigin.angularPosition;
	localOrigin.angularPosition.Add(old_position, position_delta);
	Check_Fpu();
	//
	//-----------------------------------
	// Calculate our velocity as v += a*t
	//-----------------------------------
	//
	worldLinearVelocity.AddScaled(
		worldLinearVelocity,
		worldLinearAcceleration,
		time_slice
	);
	Check_Fpu();
	localVelocity.angularMotion.AddScaled(
		localVelocity.angularMotion,
		localAcceleration.angularMotion,
		time_slice
	);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::CalculateDrag(
		Vector3D *drag,
		const Vector3D &velocity,
		const Vector3D &positive_CODs,
		const Vector3D &negative_CODs,
		Scalar power
	)
{
	Environment *air = GetEnvironment();
	Check(air);
	Vector3D temp,temp2;
	temp.MultiplyByInverse(air->GetWindVelocity(), localToWorld);
	temp += velocity;

	if (temp.x < 0.0f)
	{
		drag->x = negative_CODs.x;
		temp2.x = Power(-temp.x, power);
		Check_Fpu();
	}
	else
	{
		drag->x = -positive_CODs.x;
		temp2.x = Power(temp.x, power);
		Check_Fpu();
	}

	if (temp.y < 0.0f)
	{
		drag->y = negative_CODs.y;
		temp2.y = Power(-temp.y, power);
		Check_Fpu();
	}
	else
	{
		drag->y = -positive_CODs.y;
		temp2.y = Power(temp.y, power);
		Check_Fpu();
	}

	if (temp.z < 0.0f)
	{
		drag->z = negative_CODs.z;
		temp2.z = Power(-temp.z, power);
		Check_Fpu();
	}
	else
	{
		drag->z = -positive_CODs.z;
		temp2.z = Power(temp.z, power);
		Check_Fpu();
	}

	*drag *= air->airDensity;
	drag->Multiply(*drag, temp2);

	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::ApplyAirResistanceAndGravity(Scalar power)
{
	Check(this);

	//
	//-------------------------------------------------------------------------
	// Apply drag to the system, allowing for different drag numbers based upon
	// the direction of motion along the axis
	//-------------------------------------------------------------------------
	//
	Vector3D acceleration;
	CalculateDrag(
		&acceleration,
		localVelocity.linearMotion,
		positiveLinearDragCoefficients,
		negativeLinearDragCoefficients,
		power
	);
	localAcceleration.linearMotion += acceleration;

	acceleration.Multiply(angularDragCoefficients, localVelocity.angularMotion);
	localAcceleration.angularMotion -= acceleration;

	//
	//---------------------------
	// Apply gravity to the craft
	//---------------------------
	//
	UpdateWorldMotion();
	worldLinearAcceleration.y -= GetEnvironment()->gravityConstant;
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::ApplyLocalForce(
		const Vector3D &force,
		const Vector3D &moment
	)
{
	Check(this);
	Check(&force);
	Check(&moment);

	Vector3D acceleration;
	Verify(!Small_Enough(moverMass));
	acceleration.Divide(force, moverMass);
	ApplyLocalAcceleration(acceleration, moment);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::ApplyLocalAcceleration(
		const Vector3D &acceleration,
		const Vector3D &moment
	)
{
	Check(this);
	Check(&acceleration);
	Check(&moment);

	localAcceleration.linearMotion += acceleration;
	Vector3D torque;
	torque.Cross(moment, acceleration);
	torque *= momentOfInertia;
	localAcceleration.angularMotion += torque;
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	Mover::NoDeadReckoner()
{
	Check(this);

	//
	//-------------------------------------------------------------------------
	// If we are the replicant instance and we are not yet past the anticipated
	// time for the next event, project out to the next event
	//-------------------------------------------------------------------------
	//
	projectedOrigin = updateOrigin;
	Check_Fpu();
	return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	Mover::LinearDeadReckoner()
{
	Check(this);
	Logical lerp_mode;
	Scalar time_slice;

	//
	//-------------------------------------------------------------------------
	// If we are the replicant instance and we are not yet past the anticipated
	// time for the next event, project out to the next event
	//-------------------------------------------------------------------------
	//
	if (GetInstance() == ReplicantInstance && lastPerformance < nextUpdate)
	{
		time_slice = nextUpdate - lastUpdate;
		lerp_mode = True;
	}
	else
	{
		time_slice = lastPerformance - lastUpdate;
		lerp_mode = False;
	}

	//
	//---------------------------------------
	// Calculate the new position as p += v*t
	//---------------------------------------
	//
	Vector3D position_delta;
	position_delta.Multiply(updateVelocity.linearMotion, time_slice);
	projectedOrigin.linearPosition.Add(
		updateOrigin.linearPosition,
		position_delta
	);

	//
	//-------------------------------
	// Handle projecting the rotation
	//-------------------------------
	//
	position_delta.Multiply(updateVelocity.angularMotion, time_slice);
	projectedOrigin.angularPosition.Add(
		updateOrigin.angularPosition,
		position_delta
	);
	projectedVelocity = updateVelocity;

   Check_Fpu();
	return lerp_mode;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	Mover::AcceleratedDeadReckoner()
{
	Check(this);
	Logical lerp_mode;
	Scalar time_slice;

	//
	//-------------------------------------------------------------------------
	// If we are the replicant instance and we are not yet past the anticipated
	// time for the next event, project out to the next event
	//-------------------------------------------------------------------------
	//
	if (GetInstance() == ReplicantInstance && lastPerformance < nextUpdate)
	{
		time_slice = nextUpdate - lastUpdate;
		lerp_mode = True;
	}
	else
	{
		time_slice = lastPerformance - lastUpdate;
		lerp_mode = False;
	}

	//
	//--------------------------------------------------
	// Calculate the new position as p += v*t + a*.5*t*t
	//--------------------------------------------------
	//
	Scalar half_t_squared = 0.5f * time_slice * time_slice;
	Vector3D position_delta;
	position_delta.Multiply(updateAcceleration.linearMotion, half_t_squared);
	position_delta.AddScaled(
		position_delta,
		updateVelocity.linearMotion,
		time_slice
	);
	projectedOrigin.linearPosition.Add(
		updateOrigin.linearPosition,
		position_delta
	);

	//
	//-------------------------------
	// Handle projecting the rotation
	//-------------------------------
	//
	position_delta.Multiply(updateAcceleration.angularMotion, half_t_squared);
	position_delta.AddScaled(
		position_delta,
		updateVelocity.angularMotion,
		time_slice
	);
	projectedOrigin.angularPosition.Add(
		updateOrigin.angularPosition,
		position_delta
	);

	//
	//-----------------------------------
	// Calculate our velocity as v += a*t
	//-----------------------------------
	//
	if (GetInstance() == ReplicantInstance)
	{
		projectedVelocity.linearMotion.AddScaled(
			updateVelocity.linearMotion,
			worldLinearAcceleration,
			time_slice
		);
		projectedVelocity.angularMotion.AddScaled(
			updateVelocity.angularMotion,
			localAcceleration.angularMotion,
			time_slice
		);
	}
   Check_Fpu();
	return lerp_mode;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::DeadReckon(Scalar time_slice)
{
	Check(this);

	//
	//------------------------------
	// Run the chosen dead reckoning
	//------------------------------
	//
	Verify(GetInstance() == ReplicantInstance);
	if (deadReckoner)
	{
		//
		//---------------------------------------------------------------------
		// Merge the projected origin with the current origin if we are in lerp
		// mode.  If not, just copy the projected origin into the local origin
		//---------------------------------------------------------------------
		//
		if ((this->*deadReckoner)())
		{
			Scalar percent =
				time_slice / ((nextUpdate - lastPerformance) + time_slice);

			//
			//------------------------------------------
			// Do a spherical lerp on the angular motion
			//------------------------------------------
			//
			localOrigin.angularPosition.Lerp(
				localOrigin.angularPosition,
				projectedOrigin.angularPosition,
				percent
			);
			localVelocity.angularMotion.Lerp(
				localVelocity.angularMotion,
				projectedVelocity.angularMotion,
				percent
			);

			//
			//-------------------------
			// Spline the linear motion
			//-------------------------
			//
			#if 0
				CubicCurve
					spline(
						localOrigin.linearPosition,
						worldLinearVelocity,
						projectedOrigin.linearPosition,
						projectedVelocity.linearMotion
					);
				spline.Evaluate(
					percent,
					&localOrigin.linearPosition,
					&worldLinearVelocity
				);
			#else
				localOrigin.linearPosition.Lerp(
					localOrigin.linearPosition,
					projectedOrigin.linearPosition,
					percent
				);
				worldLinearVelocity.Lerp(
					worldLinearVelocity,
					projectedVelocity.linearMotion,
					percent
				);
			#endif
		}
		else
		{
			localOrigin = projectedOrigin;
			worldLinearVelocity = projectedVelocity.linearMotion;
			localVelocity.angularMotion = projectedVelocity.angularMotion;
		}

		//
		//----------------------------------------------------
		// Update the collision volume and the local variables
		//----------------------------------------------------
		//
		if (IsCollisionVolume())
		{
			MoveCollisionVolume();
		}
		else
		{
			localToWorld = localOrigin;
		}
		UpdateLocalMotion();
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::PerformAndWatch(
		const Time &till,
		MemoryStream *update_stream
	)
{
	Check(this);
	Check(&till);

	int i;

	//
	//--------------------------------------------------------------------------
	// Make sure that the time into the simulations is stable.  If a half-second
	// delay occurs, or we are in stasis, just bring everything up to date
	//--------------------------------------------------------------------------
	//
	Scalar time_slice = till - lastPerformance;
	if (time_slice < SMALL)
	{
		Tell("No time!\n");
Bye_Bye:
		WriteSimulationUpdate(update_stream);
		return;
	}

	if (GetSimulationState() == StasisState || time_slice > 0.5f)
	{
		lastPerformance = till;
		if (GetSimulationState() == StasisState)
		{
			lastUpdate = till;
		}
		if (subsystemArray)
		{
			Check_Pointer(subsystemArray);
			for (i=0; i<subsystemCount; ++i)
			{
				if (subsystemArray[i])
				{
					Check(subsystemArray[i]);
					subsystemArray[i]->SetLastPerformance(till);
				}
			}
		}
		//SetSimulationState(DefaultState);
		goto Bye_Bye;
	}

	//
	//------------------------------------
	// Set up for local motion calculation
	//------------------------------------
	//
	localVelocity.linearMotion.MultiplyByInverse(
		worldLinearVelocity,
		localToWorld
	);
	localAcceleration = Motion::Identity;
	previousOrigin = localOrigin;

	//
	//-----------------------
	// Process the subsystems
	//-----------------------
	//
	Entity::PerformAndWatch(till, update_stream);

	//
	//-----------------------------------------------
	// Make sure the position quaternion stays stable
	//-----------------------------------------------
	//
	if (++normalizeCount == 20)
	{
		localOrigin.angularPosition.Normalize();
		normalizeCount = 0;
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::ReadUpdateRecord(Simulation::UpdateRecord *record)
{
	Check(this);
	Check_Pointer(record);
	switch (record->recordID)
	{
		case DefaultUpdateModelBit:
		{

			//
			//-------------------------------------------
			// HACK - precalculation for next update time
			//-------------------------------------------
			//
			nextUpdate = Now();
			Scalar diff = nextUpdate - lastUpdate;
			if (diff < 10.0f)
			{
				nextUpdate.ticks += nextUpdate.ticks - lastUpdate.ticks;
			}

			//
			//---------------------------------------
			// Handle updating the entity information
			//---------------------------------------
			//
			Entity::ReadUpdateRecord(record);

			//
			//-----------------------
			// Update the motion data
			//-----------------------
			//
			UpdateRecord *update = (UpdateRecord*)record;

			localAcceleration = update->localAcceleration;
			worldLinearAcceleration = update->worldLinearAcceleration;

			updateVelocity.linearMotion = update->worldLinearVelocity;
			updateVelocity.angularMotion = update->localVelocity.angularMotion;

			updateAcceleration.linearMotion = update->worldLinearAcceleration;
			updateAcceleration.angularMotion = update->localAcceleration.angularMotion;

			//
			//-----------------------------------------
			// Update the collision volume if necessary
			//-----------------------------------------
			//
			if (IsCollisionVolume())
			{
				MoveCollisionVolume();
			}
		}
		break;

	default:
		Entity::ReadUpdateRecord(record);
		break;
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::WriteUpdateRecord(
		Simulation::UpdateRecord *record,
		int update_model
	)
{
	Check(this);
	Check_Pointer(record);
	switch (update_model)
	{
		case DefaultUpdateModelBit:
		{
			Entity::WriteUpdateRecord(record, update_model);

			UpdateRecord *update = (UpdateRecord*)record;

			update->recordLength = sizeof(*update);
			update->localVelocity = localVelocity;
			update->localAcceleration = localAcceleration;

			update->worldLinearVelocity = worldLinearVelocity;
			update->worldLinearAcceleration = worldLinearAcceleration;

			updateVelocity.linearMotion = worldLinearVelocity;
			updateVelocity.angularMotion = localVelocity.angularMotion;

			updateAcceleration.linearMotion = worldLinearAcceleration;
			updateAcceleration.angularMotion = localAcceleration.angularMotion;
		}
		break;
	default:
		Entity::WriteUpdateRecord(record, update_model);
		break;
	}
	Check_Fpu();
}

//#############################################################################
// Collision support
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::MoveCollisionVolume()
{
	//
	//---------------------------------------------------
	// Make sure that there is a collision volume to move
	//---------------------------------------------------
	//
	Check(this);
	if (!collisionVolumeCount)
	{
		Check_Fpu();
		return;
	}

	//
	//------------------------------------------------------------------------
	// Set up the extents of the collision volume from the template and the
	// current position.  We must find the center point of the template volume
	// and rotate it about the y axis
	//------------------------------------------------------------------------
	//
	Check(collisionTemplate);
	Check(collisionVolume);
	Verify(collisionVolumeCount == 1);
	Verify(collisionTemplate->solidType == BoxedSolid::YAxisCylinderType);

	localToWorld = localOrigin;
	Point3D centerPoint;
	centerPoint.x = (collisionTemplate->minX + collisionTemplate->maxX) * 0.5f;
	centerPoint.y = (collisionTemplate->minY + collisionTemplate->maxY) * 0.5f;
	centerPoint.z = (collisionTemplate->minZ + collisionTemplate->maxZ) * 0.5f;
	Vector3D radius;
	radius.x = collisionTemplate->maxX - centerPoint.x;
	radius.y = collisionTemplate->maxY - centerPoint.y;
	radius.z = collisionTemplate->maxZ - centerPoint.z;
	Point3D rotated;
	rotated.Multiply(centerPoint, localToWorld);

	collisionVolume->minX = rotated.x - radius.x;
	collisionVolume->maxX = rotated.x + radius.x;
	collisionVolume->minY = rotated.y - radius.y;
	collisionVolume->maxY = rotated.y + radius.y;
	collisionVolume->minZ = rotated.z - radius.z;
	collisionVolume->maxZ = rotated.z + radius.z;

	//
	//------------------------------------------------------------
	// Now, Find the smallest node containing our collision column
	//------------------------------------------------------------
	//
	if (GetInstance() != ReplicantInstance)
	{
		InterestManager *interest_mgr =
			application->GetInterestManager();
		Check(interest_mgr);
		InterestZone *zone =
			interest_mgr->GetInterestZone(interestZoneID);
		Check(zone);
		BoxedSolidTree* tree = zone->GetCollisionRoot();
		Check(tree);
		containedByNode =
			tree->FindSmallestNodeContainingColumn(*collisionVolume);
	}
   Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolidCollisionList*
	Mover::AllocateCollisionList()
{
	Check(this);

	//
	//-----------------------------------------------------------
	// Find the correct collision list to use, and reset to empty
	//-----------------------------------------------------------
	//
	BoxedSolidCollisionList *collision_list;
	if (lastCollisionList == collisionLists)
	{
		collision_list = &collisionLists[1];
	}
	else
	{
		collision_list = collisionLists;
	}
	Check(collision_list);
	collision_list->Reset();
	return collision_list;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolidCollisionList*
	Mover::GetCurrentCollisions(BoxedSolidCollisionList *collision_list)
{
	Check(this);

	if (!collision_list)
	{
		collision_list = AllocateCollisionList();
	}
	Check(collision_list);

	//
	//---------------------------------
	// Test against the tangible movers
	//---------------------------------
	//
	Check(collisionAssistant);
	CollisionAssistant::MovingEntityIterator iterator(collisionAssistant);
	Entity *entity;
	Check(collisionVolume);
	while ((entity = iterator.ReadAndNext()) != NULL)
	{
		//
		//------------------------------------------------------------------
		// If we are checking against ourselves, or something more than 50m
		// away, skip it
		//------------------------------------------------------------------
		//
		Check(entity);
		if (entity == this)
		{
			continue;
		}
		Vector3D delta;
		delta.Subtract(
			entity->localOrigin.linearPosition,
			localOrigin.linearPosition
		);
		if (delta.LengthSquared() > 2500.0f)
		{
			continue;
		}

		//
		//--------------------------------------------------
		// If we have a mover class object, check against it
		//--------------------------------------------------
		//
		if (entity->IsDerivedFrom(Mover::ClassDerivations))
		{
			Mover *mover = (Mover*)entity;
			Check(mover);
			Check(mover->collisionVolume);
			CheckAgainstBoxedSolidChain(collision_list, mover->collisionVolume);
		}

		//
		//-----------------------------------------------------------------------
		// If we have a door, check against its subsystems if we are close enough
		// for it to matter
		//-----------------------------------------------------------------------
		//
		else if (entity->IsDerivedFrom(DoorFrame::ClassDerivations))
		{
			DoorFrame *door_frame = (DoorFrame*)entity;
			Check(door_frame);
			for (int i=0; i<door_frame->GetSubsystemCount(); ++i)
			{
				Door *door = (Door*)door_frame->GetSubsystem(i);
				CheckAgainstBoxedSolidChain(
					collision_list,
					door->GetFirstBoxedSolid()
				);
			}
		}
	}

	//
	//------------------------------
	// Test against the static world
	//------------------------------
	//
	containedByNode->FindBoundingBoxesContaining(
		collisionVolume,
		*collisionVolume,
		*collision_list
	);

   Check_Fpu();
	return collision_list;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolid*
	Mover::FindBoxedSolidHitBy(
		Line *line,
		Entity *except
	)
{
	Check(this);
	Check(line);

	//
	//-------------------------------------------------------------------------
	// Calculate the midpoint of the line, and sweep a sphere out around the
	// line from that point, including an extra 50 meters.  This extra distance
	// takes into account the doors...
	//-------------------------------------------------------------------------
	//
	Point3D center;
	Scalar radius = line->length * 0.5f;
	line->Project(radius, &center);

	//
	//---------------------------------
	// Test against the tangible movers
	//---------------------------------
	//
	Check(collisionAssistant);
	CollisionAssistant::MovingEntityIterator iterator(collisionAssistant);
	Entity *entity;
	BoxedSolid
		*solid = NULL,
		*result;

	while ((entity = iterator.ReadAndNext()) != NULL)
	{
		//
		//---------------------------------------------------------------
		// If we are checking against ourselves or the exception, skip it
		//---------------------------------------------------------------
		//
		Check(entity);
		if (entity == this || except && except == entity)
		{
			continue;
		}

		//
		//-------------------------------------------------------------------
		// If we have a mover class object, check against it.  If we have no
		// collision volume, we are just using a line, so just run it against
		// the collision volume chain
		//-------------------------------------------------------------------
		//
		if (entity->IsDerivedFrom(Mover::ClassDerivations))
		{
			Mover *mover = (Mover*)entity;
			Check(mover);
			Check(mover->collisionVolume);

			//
			//-----------------------------------------------------------------
			// If the mover is close enough to the radius of the line, check it
			//-----------------------------------------------------------------
			//
			Vector3D delta;
			delta.Subtract(entity->localOrigin.linearPosition, center);
			Scalar r2 =
				mover->collisionVolume->maxX - mover->collisionVolume->minX;
			r2 += mover->collisionVolume->maxY - mover->collisionVolume->minY;
			r2 *= 0.5f;
			r2 += radius;
			if (delta.LengthSquared() > r2*r2)
			{
				continue;
			}

			result =
				CheckLineAgainstBoxedSolidChain(line, mover->collisionVolume);
			if (result)
			{
				Check(result);
				solid = result;
			}
		}

		//
		//-----------------------------------------------------------------------
		// If we have a door, check against its subsystems if we are close enough
		// for it to matter
		//-----------------------------------------------------------------------
		//
		else if (entity->IsDerivedFrom(DoorFrame::ClassDerivations))
		{
			//
			//-----------------------------------------------------------------
			// If the mover is close enough to the radius of the line, check it
			//-----------------------------------------------------------------
			//
			Vector3D delta;
			delta.Subtract(entity->localOrigin.linearPosition, center);
			Scalar r2 = radius + 50.0f;
			if (delta.LengthSquared() > r2*r2)
			{
				continue;
			}

			DoorFrame *door_frame = (DoorFrame*)entity;
			Check(door_frame);
			for (int i=0; i<door_frame->GetSubsystemCount(); ++i)
			{
				Door *door = (Door*)door_frame->GetSubsystem(i);
				result =
					CheckLineAgainstBoxedSolidChain(
						line,
						door->GetFirstBoxedSolid()
					);
				if (result)
				{
					Check(result);
					solid = result;
				}
			}
		}
	}

	//
	//------------------------------
	// Test against the static world
	//------------------------------
	//
	InterestManager *interest_mgr = application->GetInterestManager();
	Check(interest_mgr);
	InterestZone *zone = interest_mgr->GetInterestZone(interestZoneID);
	Check(zone);
	BoxedSolidTree* tree = zone->GetCollisionRoot();
	Check(tree);
	result = (BoxedSolid*)tree->FindBoundingBoxHitBy(line);
	if (result)
	{
		Check(result);
		solid = result;
	}
	Check_Fpu();
	return solid;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolidCollisionList*
	Mover::CollideCenterOfMotion(
		Line *line,
		BoxedSolidCollisionList *list
	)
{
	Check(this);
	Check(line);

	//
	//-----------------------------------------------------------
	// Find the correct collision list to use, and reset to empty
	//-----------------------------------------------------------
	//
	if (!list)
	{
		list = AllocateCollisionList();
	}
	Check(list);

	//
	//------------------------------------------------------------------------
	// If the length of the line has changed, we must reposition the collision
	// volume appropriately
	//------------------------------------------------------------------------
	//
	BoxedSolid *solid = FindBoxedSolidHitBy(line, NULL);
	if (solid && IsCollisionVolume())
	{
		line->FindEnd(&localOrigin.linearPosition);
		MoveCollisionVolume();
		ExtentBox slice;
		slice.Intersect(*collisionVolume, *solid);
		Verify(list->GetCollisionsLeft());
		list->AddCollisionToList(solid, slice);
	}

	Check_Fpu();
	return list;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::ProcessCollisionList(
		BoxedSolidCollisionList *collisions,
		Scalar time_slice,
		const Point3D &old_position,
		Damage *damage
	)
{
	Check(this);
	Check(collisions);
	Verify(time_slice > 0.0f);
	Check(&old_position);
	Check_Pointer(damage);

	damage->damageAmount = 0.0f;
	damage->damageType = Damage::CollisionDamageType;
	damage->impactPoint = Point3D::Identity;

	if (collisions->GetCollisionCount())
	{
		//
		//------------------------------------------------------------------
		// Reduce the number of collisions we have to play with based on our
		// velocity
		//------------------------------------------------------------------
		//
		collisions->ReduceCollisionList(worldLinearVelocity);

		//
		//-----------------------------------------------------------------
		// Setup up the totaling variables to handle averaging out multiple
		// collisions
		//-----------------------------------------------------------------
		//
		int total_collisions = 0;
		Vector3D resultant_velocity = Vector3D::Identity;
		Point3D resultant_position = Point3D::Identity;
		Vector3D resultant_normal = Vector3D::Identity;
		Vector3D initial_velocity = worldLinearVelocity;
		Vector3D initial_position = localOrigin.linearPosition;
		Scalar total_damage = 0.0f;

		//
		//---------------------------------------------------------------------
		// For each hit in the list, process it, and if the collision is
		// determined to be valid, bounce it and add the result into the others
		//---------------------------------------------------------------------
		//
		for (int i=0; i<collisions->GetRealCollisions(); ++i)
		{
			//
			//----------------------------------------------------------
			// Make sure to bounce the vehicle from the correct location
			//----------------------------------------------------------
			//
			worldLinearVelocity = initial_velocity;
			localOrigin.linearPosition = initial_position;
			damage->damageAmount = 0.0f;
			ProcessCollision(
				time_slice,
				(*collisions)[i],
				old_position,
				damage
			);
			if (damage->damageAmount > 0.0f)
			{
				++total_collisions;
				resultant_velocity += worldLinearVelocity;
				resultant_position += localOrigin.linearPosition;
				resultant_normal += damage->surfaceNormal;
				total_damage += damage->damageAmount;
				ExtentBox *box = &(*collisions)[i].collisionSlice;
				damage->impactPoint.x += 0.5 *
					(
						box->minX + box->maxX
						- (collisionVolume->minX - collisionVolume->maxX)
					);
				damage->impactPoint.y += 0.5 *
					(
						box->minY + box->maxY
						- (collisionVolume->minY - collisionVolume->maxY)
					);
				damage->impactPoint.z += 0.5 *
					(
						box->minZ + box->maxZ
						- (collisionVolume->minZ - collisionVolume->maxZ)
					);
			}
		}

		//
		//-----------------------------------------------------------------
		// If we collided with more than one thing, average out the results
		//-----------------------------------------------------------------
		//
		if (total_collisions > 1)
		{
			worldLinearVelocity.Divide(resultant_velocity, total_collisions);
			localOrigin.linearPosition.Divide(
				resultant_position,
				total_collisions
			);
			damage->surfaceNormal.Vector3D::Divide(
				resultant_normal,
				total_collisions
			);
			goto Figure_Normal;
		}

		//
		//------------------------------------------------------
		// Otherwise, just set up the positions from the results
		//------------------------------------------------------
		//
		else if (total_collisions == 1)
		{
			worldLinearVelocity = resultant_velocity;
			localOrigin.linearPosition = resultant_position;
			damage->surfaceNormal.operator=(resultant_normal);

			//
			//---------------------------------------------------------
			// Figure out the normal, and calculate the collision force
			//---------------------------------------------------------
			//
Figure_Normal:
			if (Small_Enough(damage->surfaceNormal.LengthSquared()))
			{
				damage->surfaceNormal.x = 0.0f;
				damage->surfaceNormal.y = 1.0f;
				damage->surfaceNormal.z = 0.0f;
			}
			else
			{
				damage->surfaceNormal.Normalize(damage->surfaceNormal);
			}
			MoveCollisionVolume();
			damage->damageAmount = total_damage;
			damage->damageForce.Subtract(worldLinearVelocity, initial_velocity);
		}
		lastCollisionList = collisions;
	}
	else
	{
		lastCollisionList = NULL;
	}
   Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::ProcessCollision(
		Scalar time_slice,
		BoxedSolidCollision &collision,
		const Point3D &old_position,
		Damage *damage
	)
{
	Check(this);
	Verify(time_slice > 0.0f);
	Check(&collision);
	Check(&old_position);
	Check_Pointer(damage);

	Scalar penetration;

	//
	//------------------------------------------------------------------------
	// If we really have a collision, do a static bounce off of the normal
	// generated.  This is default behavior, and any derived class should make
	// sure to handle any handshaking that needs to be done
	//------------------------------------------------------------------------
	//
	if (
		collisionVolume->ProcessCollision(
			collision,
			worldLinearVelocity,
			lastCollisionList,
			&damage->surfaceNormal,
			&penetration
		)
	)
	{
		Max_Clamp(penetration, time_slice);
		Scalar r = penetration / time_slice;
		Scalar elasticity = elasticityCoefficient;
		Scalar friction = frictionCoefficient;

		damage->damageAmount =
			StaticBounce(
				old_position,
				time_slice,
				r,
				damage->surfaceNormal,
				&elasticity,
				minimumBounceSpeed,
				&friction
			);
	}
   Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::StartCollisionAssistant()
{
	Check(this);
	Verify(collisionAssistant == NULL);

	collisionAssistant = CollisionAssistant::Make(this);
	Register_Object(collisionAssistant);

	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	Mover::StaticBounce(
		const Point3D &, //old_position,
		Scalar delta_t,
		Scalar penetration,
		const Normal &normal,
		Scalar *elasticity,
		Scalar bounce_min,
		Scalar *friction
	)
{
	Check(this);
	Check(&normal);
	Check_Pointer(elasticity);
	Check_Pointer(friction);

	Verify(penetration >= 0.0f && penetration <= 1.0f);
	Verify(*elasticity >= 0.0f && *elasticity <= 1.0f);
	Verify(*friction >= 0.0f);
	Verify(delta_t > SMALL);

//      penetration = 1.0f;             // HACK - should keep stuff from going through the floor

	//
	//-----------------------------------------------------------------------
	// Calculate the impact speed and vectors.  If we didn't hit fast enough,
	// don't do any bounce
	//-----------------------------------------------------------------------
	//
	Scalar impact = worldLinearVelocity * normal;
	Vector3D vn,vp;
	vn.Multiply(normal, impact);
	vp.Subtract(worldLinearVelocity, vn);
	if (impact > 0.0f)
	{
		Check_Fpu();
		return 0.0f;
	}
	if (-impact <= bounce_min * delta_t)
	{
		*elasticity = 0.0f;
	}

	//
	//--------------------------------------
	// Calculate the energy lost to friction
	//--------------------------------------
	//
	Scalar resistance = vp.Length();
	if (Small_Enough(resistance))
	{
		*friction = resistance = 0.0f;
	}
	else
	{
		resistance =
			1.0f + *friction * (1.0f + *elasticity) * impact / resistance;
		if (resistance < 0.0f)
		{
			*friction = resistance = 0.0f;
		}
	}

	//
	//----------------------------------------------------
	// Compute the velocity delta created by the collision
	//----------------------------------------------------
	//
	Scalar temp = resistance - 1.0f;
	Vector3D delta_v;
	delta_v.Multiply(worldLinearVelocity, temp);
	temp = resistance + *elasticity;
	delta_v.AddScaled(delta_v, vn, -temp);

	//
	//------------------------------------
	// Figure out the kinetic energy stuff
	//------------------------------------
	//
	temp = -1.0f - *elasticity;
	vn *= temp;
	vp.AddScaled(vn, worldLinearVelocity, 2.0f);

	//
	// Reflect the velocity vector
	//
	worldLinearVelocity += delta_v;
	temp = penetration * delta_t;
	delta_v *= temp;
	localOrigin.linearPosition += delta_v;

	//
	// Compute the kinetic energy loss
	//
	Check_Fpu();
	return -0.0005 * (vn * vp) * moverMass;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	Mover::DynamicBounce(
		Mover *other,
		Scalar delta_t,
		Scalar penetration,
		const Normal &normal,
		Scalar *elasticity
	)
{
	Check(this);
	Check(other);
	Check(&normal);
	Check_Pointer(elasticity);

	Verify(penetration >= 0.0f && penetration <= 1.0f);
	Verify(*elasticity >= 0.0f && *elasticity <= 1.0f);
	Verify(delta_t > SMALL);

	//
	//------------------------------------------------------------------------
	// Get the relative velocity of the other guy, and figure out the velocity
	// delta along the normal
	//------------------------------------------------------------------------
	//
	Scalar k1 = worldLinearVelocity.LengthSquared();
	Scalar k2 = other->worldLinearVelocity.LengthSquared();

	Scalar mass_ratio = other->moverMass / (moverMass + other->moverMass);
	Check_Fpu();
	Vector3D v;
	v.Subtract(other->worldLinearVelocity, worldLinearVelocity);
	Scalar temp = (1.0f + *elasticity) * (v*normal);
	Vector3D delta_v;
	delta_v.Multiply(normal, temp);

	//
	//-------------------------------------------------------------------------
	// Figure out the kinetic energy loss in kilojoules, and bounce the primary
	// mover
	//
	// There was an additional multiplication by mass ratio in system 3 code...
	//   we should make sure it is really needed...
	//-------------------------------------------------------------------------
	//
	v.AddScaled(delta_v, v, -2.0f);
	worldLinearVelocity.AddScaled(
		worldLinearVelocity,
		delta_v,
		mass_ratio
	);
	localOrigin.linearPosition.AddScaled(
		localOrigin.linearPosition,
		delta_v,
		delta_t * penetration
	);

	//
	//----------------------------------------------------------
	// Bounce the second object, and reset it's update values...
	//----------------------------------------------------------
	//
	other->worldLinearVelocity.AddScaled(
		other->worldLinearVelocity,
		delta_v,
		mass_ratio - 1.0f
	);
	other->localOrigin.linearPosition.AddScaled(
		other->localOrigin.linearPosition,
		delta_v,
		delta_t
	);
	other->updateVelocity.linearMotion = other->worldLinearVelocity;
	other->updateOrigin.linearPosition = other->localOrigin.linearPosition;
	other->lastUpdate = Now();

	//
	//--------------------------------
	// Return the result in kilojoules
	//--------------------------------
	//
	k1 -= worldLinearVelocity.LengthSquared();
	k2 -= other->worldLinearVelocity.LengthSquared();
   Check_Fpu();
	return 0.0005f * mass_ratio * (moverMass * k1 + other->moverMass * k2);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::CheckAgainstBoxedSolidChain(
		BoxedSolidCollisionList *collisions,
		BoxedSolid *chain
	)
{
	Check(this);
	Check(collisions);

	//
	//----------------------------------------------------------------------
	// If the two movers collided against with each other, add the result to
	// the collision list
	//----------------------------------------------------------------------
	//
	while (chain)
	{
		Check(chain);
		ExtentBox slice;
		if (chain->Intersects(*collisionVolume, &slice))
		{
			Verify(collisions->GetCollisionsLeft());
			collisions->AddCollisionToList(chain, slice);
			if (!collisions->GetCollisionsLeft())
			{
				return;
			}
		}
		chain = chain->GetNextSolid();
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Mover::CheckVolumeAgainstBoxedSolidChain(
		BoxedSolidCollisionList *collisions,
		BoxedSolid *chain
	)
{
	Check(this);
	Check(collisions);

	//
	//----------------------------------------------------------------------
	// If the two movers collided against with each other, add the result to
	// the collision list
	//----------------------------------------------------------------------
	//
	while (chain)
	{
		Check(chain);
		ExtentBox slice;
		if (chain->Intersects(*collisionVolume, &slice))
		{
			Verify(collisions->GetCollisionsLeft());
			collisions->AddCollisionToList(collisionVolume, slice);
			if (!collisions->GetCollisionsLeft())
			{
				return;
			}
		}
		chain = chain->GetNextSolid();
	}
	Check_Fpu();
}



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolid*
	Mover::CheckLineAgainstBoxedSolidChain(
		Line *line,
		BoxedSolid *chain
	)
{
	Check(this);
	Check(line);

	//
	//----------------------------------------------------------------------
	// If the two movers collided against with each other, add the result to
	// the collision list
	//----------------------------------------------------------------------
	//
	BoxedSolid *result = NULL;
	while (chain)
	{
		Check(chain);
		if (chain->HitBy(line))
		{
			result = chain;
		}
		chain = chain->GetNextSolid();
	}
   Check_Fpu();
	return result;
}

//#############################################################################
// Construction and Destruction
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Mover::Mover(
	Mover::MakeMessage *creation_message,
	Mover::SharedData &virtual_data
):
	Entity(creation_message, virtual_data)
{
	Check_Pointer(this);
	Check(creation_message);

	Check(application);
	ResourceFile *res_file = application->GetResourceFile();
	Check(res_file);

	//
	//------------------------------
	// Initialize the motion vectors
	//------------------------------
	//
	localVelocity = creation_message->localVelocity;
	localAcceleration = creation_message->localAcceleration;
	worldLinearAcceleration.Multiply(
		localAcceleration.linearMotion,
		localToWorld
	);
	worldLinearVelocity.Multiply(
		localVelocity.linearMotion,
		localToWorld
	);

	updateVelocity.linearMotion = worldLinearVelocity;
	updateVelocity.angularMotion = localVelocity.linearMotion;

	updateAcceleration.linearMotion = worldLinearAcceleration;
	updateAcceleration.angularMotion = localAcceleration.linearMotion;
	nextUpdate = lastUpdate;

	normalizeCount = 0;
	if (IsInitialStasis())
	{
		SetSimulationState(StasisState);
	}

	collisionVolume = NULL;
	collisionTemplate = NULL;
	containedByNode = NULL;
	collisionLists = NULL;
	lastCollisionList = NULL;
	collisionAssistant = NULL;
	deadReckoner = NULL;
	collisionVolumeCount = 0;

	ResourceDescription *res =
		res_file->SearchList(
			resourceID,
			ResourceDescription::GameModelResourceType
		);
	Check(res);
	res->Lock();
	ModelResource* model = (ModelResource*)res->resourceAddress;
	Check_Pointer(model);

	moverMass = model->moverMass;
	Verify(!Small_Enough(model->momentOfInertia.x));
	momentOfInertia.x = 1.0f/model->momentOfInertia.x;
	Verify(!Small_Enough(model->momentOfInertia.y));
	momentOfInertia.y = 1.0f/model->momentOfInertia.y;
	Verify(!Small_Enough(model->momentOfInertia.z));
	momentOfInertia.z = 1.0f/model->momentOfInertia.z;
	positiveLinearDragCoefficients = model->positiveLinearDragCoefficients;
	negativeLinearDragCoefficients = model->negativeLinearDragCoefficients;
	angularDragCoefficients = model->angularDragCoefficients;
	frictionCoefficient = model->frictionCoefficient;
	elasticityCoefficient = model->elasticityCoefficient;
	minimumBounceSpeed = model->minimumBounceSpeed;

	//
	//--------------------------------------------------------------------
	// Read the collision information from the resource file, but for now,
	// assume a VTV
	//--------------------------------------------------------------------
	//
	collisionLists = new BoxedSolidCollisionList[2];
	Register_Pointer(collisionLists);
	res->Unlock();
	if (IsCollisionVolume())
	{
		res =
			res_file->SearchList(
				resourceID,
				ResourceDescription::BoxedSolidStreamResourceType
			);
		Check(res);
		res->Lock();

		BoxedSolidResource* box = (BoxedSolidResource*)res->resourceAddress;
		Check_Pointer(box);
		collisionVolumeCount = res->resourceSize / sizeof(BoxedSolidResource);

		for (int i=0; i<collisionVolumeCount; ++i)
		{
			collisionTemplate =
				BoxedSolid::MakeBoxedSolid(box, this, collisionTemplate);
			Register_Object(collisionTemplate);

			collisionVolume =
				BoxedSolid::MakeBoxedSolid(box, this, collisionVolume);
			Register_Object(collisionVolume);

			++box;
		}

		res->Unlock();
		MoveCollisionVolume();
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	Mover::CreateMakeMessage(
		MakeMessage *creation_message,
		NotationFile *model_file,
		const ResourceDirectories *directories
	)
{
	Check(creation_message);
	Check(model_file);

	if (!Entity::CreateMakeMessage(creation_message, model_file, directories))
	{
		return False;
	}

	creation_message->messageLength = sizeof(Mover::MakeMessage);
	creation_message->classToCreate = RegisteredClass::TrivialMoverClassID;
//      creation_message->instanceFlags = DefaultFlags;
	creation_message->localVelocity = Motion::Identity;
	creation_message->localAcceleration = Motion::Identity;
	Check_Fpu();
	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ResourceDescription::ResourceID
	Mover::CreateModelResource(
		ResourceFile *resource_file,
		const char* model_name,
		NotationFile *model_file,
		const ResourceDirectories *,//directories,
		ModelResource *model
	)
{
	Check(resource_file);
	Check_Pointer(model_name);
	Check(model_file);

	//
	//-----------------------------------------------------------------------
	// If we were not provided a buffer to write the model data into, we must
	// create it ourselves
	//-----------------------------------------------------------------------
	//
	ModelResource *local_model = model;
	if (!local_model)
	{
		local_model = new ModelResource;
		Register_Pointer(local_model);
	}

	//
	//-----------------
	// Read in the mass
	//-----------------
	//
	if (!model_file->GetEntry("gamedata", "MoverMass", &local_model->moverMass))
	{
		cerr << model_name << " missing MoverMass!\n";
Dump_And_Die:
		if (!model)
		{
			Unregister_Pointer(local_model);
			delete local_model;
		}
		Check_Fpu();
		return -1;
	}

	//
	//------------------------------
	// Read in the moment of inertia
	//------------------------------
	//
	const char* entry;
	if (
		!model_file->GetEntry(
			"gamedata",
			"MomentOfInertia",
			&entry
		)
	)
	{
		cerr << model_name << " missing MomentOfInertia!\n";
		goto Dump_And_Die;
	}
	sscanf(
		entry,
		"%f %f %f",
		&local_model->momentOfInertia.x,
		&local_model->momentOfInertia.y,
		&local_model->momentOfInertia.z
	);

	//
	//------------------------------
	// Read in the drag coefficients
	//------------------------------
	//
	if (
		!model_file->GetEntry(
			"gamedata",
			"PositiveLinearDragCoefficients",
			&entry
		)
	)
	{
		cerr << model_name << " missing PositiveLinearDragCoefficients!\n";
		goto Dump_And_Die;
	}
	sscanf(
		entry,
		"%f %f %f",
		&local_model->positiveLinearDragCoefficients.x,
		&local_model->positiveLinearDragCoefficients.y,
		&local_model->positiveLinearDragCoefficients.z
	);

	if (
		!model_file->GetEntry(
			"gamedata",
			"NegativeLinearDragCoefficients",
			&entry
		)
	)
	{
		cerr << model_name << " missing NegativeLinearDragCoefficients!\n";
		goto Dump_And_Die;
	}
	sscanf(
		entry,
		"%f %f %f",
		&local_model->negativeLinearDragCoefficients.x,
		&local_model->negativeLinearDragCoefficients.y,
		&local_model->negativeLinearDragCoefficients.z
	);

	//
	//-------------------------
	// Read in the angular drag
	//-------------------------
	//
	if (
		!model_file->GetEntry(
			"gamedata",
			"AngularDragCoefficients",
			&entry
		)
	)
	{
		cerr << model_name << " missing AngularDragCoefficients!\n";
		goto Dump_And_Die;
	}
	sscanf(
		entry,
		"%f %f %f",
		&local_model->angularDragCoefficients.x,
		&local_model->angularDragCoefficients.y,
		&local_model->angularDragCoefficients.z
	);

	//
	//---------------------
	// Read in the friction
	//---------------------
	//
	if (
		!model_file->GetEntry(
			"gamedata",
			"FrictionCoefficient",
			&local_model->frictionCoefficient
		)
	)
	{
		cerr << model_name << " missing FrictionCoefficient!\n";
		goto Dump_And_Die;
	}

	//
	//-----------------------
	// Read in the elasticity
	//-----------------------
	//
	if (
		!model_file->GetEntry(
			"gamedata",
			"ElasticityCoefficient",
			&local_model->elasticityCoefficient
		)
	)
	{
		cerr << model_name << " missing ElasticityCoefficient!\n";
		goto Dump_And_Die;
	}

	//
	//---------------------------------
	// Read in the minimum bounce speed
	//---------------------------------
	//
	if (
		!model_file->GetEntry(
			"gamedata",
			"MinimumBounceSpeed",
			&local_model->minimumBounceSpeed
		)
	)
	{
		cerr << model_name << " missing MinimumBounceSpeed!\n";
		goto Dump_And_Die;
	}

	//
	//-------------------------------------------------------------------------
	// If we created the model buffer, then we have the responsibility to write
	// it out to the resource file
	//-------------------------------------------------------------------------
	//
	if (!model)
	{
		ResourceDescription *new_res =
			resource_file->AddResource(
				model_name,
				ResourceDescription::GameModelResourceType,
				1,
				ResourceDescription::Preload,
				local_model,
				sizeof(*local_model)
			);
		Unregister_Pointer(local_model);
		delete local_model;
		Check(new_res);
		Check_Fpu();
		return new_res->resourceID;
	}
	else
	{
		Check_Fpu();
		return 0;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Mover*
	Mover::Make(Mover::MakeMessage *creation_message)
{
	return new Mover(creation_message, DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Mover::~Mover()
{
	Unregister_Pointer(collisionLists);
	delete[] collisionLists;

	if (IsCollisionVolume())
	{
		BoxedSolid *box = collisionTemplate;
		while (box)
		{
			BoxedSolid *next_box = box->GetNextSolid();
			Unregister_Object(box);
			delete box;
			box = next_box;
		}
		box = collisionVolume;
		while (box)
		{
			BoxedSolid *next_box = box->GetNextSolid();
			Unregister_Object(box);
			delete box;
			box = next_box;
		}
	}

	if (collisionAssistant)
	{
		Unregister_Object(collisionAssistant);
		delete collisionAssistant;
	}
	Check_Fpu();
}

Logical
	Mover::TestInstance() const
{
	return IsDerivedFrom(ClassDerivations);
}

