#include "rpl4.h"
#pragma hdrstop

#define PRELOAD_ART

#	include "rpl4gaug.h"
#	include "rpl4grnd.h"
#	include "rpl4mode.h"
#	include "rpl4mppr.h"
#	include "..\munga_l4\l4gauge.h"
#	include "..\munga_l4\l4gauima.h"
#	include "..\munga_l4\l4vb8.h"
#	include "..\munga_l4\l4ctrl.h"
#	include "..\munga_l4\l4icom.h"
#	include "..\rp\rpplayer.h"
#	include "..\munga\terrain.h"
#	include "..\munga\mission.h"
#	include "..\rp\vtv.h"
#	include "..\munga\nttmgr.h"
#	include "..\munga\app.h"

// #define LOCAL_TEST

#if defined(LOCAL_TEST)
#	define Test_Tell(n) std::cout << n
#else
#	define Test_Tell(n)
#endif

union quickCompare
{
	char ascii[4];
	long bytes4;
};

struct colorTable
{	
	quickCompare name;
	int colorValue;
};

static colorTable
	colorLookUp[] =
{
	{ {"Aqu"} , 1 },
	{ {"Bla"} , 2 },
	{ {"Blu"} , 3 },
	{ {"Gre"} , 4 },
	{ {"Pin"} , 5 },
	{ {"Pur"} , 6 },
	{ {"Red"} , 7 },
	{ {"Whi"} , 8 },
	{ {"Yel"} , 9 },
	{ {"xxx"} , -1}
};

int
  determineEntityColor(Entity *entity, Entity */*local_entity*/)
{

	const char
		*color_name;
	quickCompare
		compare;
	colorTable
		*colors;

	if (entity->IsDerivedFrom(*VTV::GetClassDerivations()))
	{
		color_name = ((VTV *) entity)->vehicleColor;
		compare.ascii[0] = *color_name++;
		compare.ascii[1] = *color_name++;
		compare.ascii[2] = *color_name;
		compare.ascii[3] = '\0';

		for (colors=colorLookUp; colors->colorValue >0; ++colors)
		{
			if (colors->name.bytes4 == compare.bytes4)
			{
				Check_Fpu();
				return colors->colorValue;
			}
		}
	}
	Check_Fpu();
	return 255;
}

Scalar
	generateRelativeYaw(Scalar my_yaw, Vector3D &relative_position)
{
	Vector3D
		delta = relative_position,
		unit;
	//---------------------------------------------------------
	// Generate unit vector pointing towards 'other'
	//---------------------------------------------------------
	delta.y = 0.0;				// this may induce an error if delta_y is large
									// (but I think we can live with it)

	Scalar
		length = delta.Length();

	if (Small_Enough(length))
	{
		unit.x = 0.0;
		unit.y = 0.0;
		unit.z = -1.0;
	}
	else
	{
		unit = delta;
		unit /= length;
		Check_Fpu();
	}
	//---------------------------------------------------------
	// Generate angle in radians
	//---------------------------------------------------------
	SinCosPair
		sc_pair(unit.x, unit.z);

	Radian
		angle;

	angle = sc_pair;
	Check_Fpu();
	//---------------------------------------------------------
	// Get relative angle
	//---------------------------------------------------------
	angle -= my_yaw;
	angle.Normalize();
	Check_Fpu();

	return angle;
}

static char
	*nameCopy(const char *old_name)
{
	Check_Pointer(old_name);

	char
		*copy = new char[strlen(old_name)+1];
	Register_Pointer(copy);
	strcpy(copy, old_name);

	return copy;
}

//*****************************************************************************
// Compass
//*****************************************************************************
MethodDescription
	Compass::methodDescription =
		{
			"compass",
			Compass::Make,
			{
				{ ParameterDescription::typeRate, NULL },		// rate ID
				{ ParameterDescription::typeModeMask, NULL },// mode mask
				{ ParameterDescription::typeInteger, NULL },	// NWES radius
				{ ParameterDescription::typeColor, NULL },	// bg color
				{ ParameterDescription::typeString, NULL },	// 'N' pixelmap
				{ ParameterDescription::typeString, NULL },	// 'W' pixelmap
				{ ParameterDescription::typeString, NULL },	// 'S' pixelmap
				{ ParameterDescription::typeString, NULL },	// 'E' pixelmap
				{ ParameterDescription::typeString, NULL },	// Bug pixelmap
				PARAMETER_DESCRIPTION_END,
			}
		};

Logical
	Compass::Make(
		int display_port_index,
		Vector2DOf<int> position,
		Entity */*entity*/,
		GaugeRenderer *gauge_renderer
	)
{
	Test_Tell("Compass::Make\n");
	Check(gauge_renderer);

	ParameterDescription
		*parameterList = methodDescription.parameterList;

#	if DEBUG_LEVEL > 0
	parameterList[0].CheckIt(ParameterDescription::typeRate);		// group ID
	parameterList[1].CheckIt(ParameterDescription::typeModeMask);	// mode mask
	parameterList[2].CheckIt(ParameterDescription::typeInteger);	// radius
	parameterList[3].CheckIt(ParameterDescription::typeColor);		// bg color
	parameterList[4].CheckIt(ParameterDescription::typeString);		// 'N' pixmap
	parameterList[5].CheckIt(ParameterDescription::typeString);		// 'W' pixmap
	parameterList[6].CheckIt(ParameterDescription::typeString);		// 'S' pixmap
	parameterList[7].CheckIt(ParameterDescription::typeString);		// 'E' pixmap
	parameterList[8].CheckIt(ParameterDescription::typeString);		// Bug pixmap
#	endif

#	if DEBUG_LEVEL > 0
	Compass
		*compass =
#	endif
			new Compass(
				parameterList[0].data.rate,
				parameterList[1].data.modeMask,
				(L4GaugeRenderer *) gauge_renderer,
				0, 										//unsigned int owner_ID
				display_port_index, 					//int graphics_port_number
				position.x, 
				position.y,
				parameterList[2].data.integer,// NWES radius
				parameterList[3].data.color,	// bg color
				parameterList[4].data.string,	// 'N' pixelmap
				parameterList[5].data.string,	// 'W' pixelmap
				parameterList[6].data.string,	// 'S' pixelmap
				parameterList[7].data.string,	// 'E' pixelmap
				parameterList[8].data.string	// Bug pixelmap
			);
	Register_Object(compass);
	//----------------------------------------------------------
	// Make sure the icons were properly placed in the warehouse
	//----------------------------------------------------------
	Check(gauge_renderer);
	L4Warehouse
		*warehouse = (L4Warehouse *) gauge_renderer->warehousePointer;
	Check(warehouse);

	for(int i=4; i<=8; ++i)
	{
		if (warehouse->pixelMap8Bin.Get(parameterList[i].data.string) == NULL)
		{
			Test_Tell(
				"Compass::Missing icon '" << parameterList[i].data.string <<
				"'\n"
			);
			Check_Fpu();
			return False;
		}
		else
		{
			warehouse->pixelMap8Bin.Release(parameterList[i].data.string);
		}
	}
	//--------------------------------------------------------
	// Everything's ok
	//--------------------------------------------------------
	Test_Tell("Compass::Make OK\n");
	Check_Fpu();
	return True;
}

void
	prepCompassMap(
		L4Warehouse *warehouse,
		const char *image_name,
		Vector2DOf<int> *offset
	)
{
	Check(warehouse);

	//-------------------------------------------------------------
	// Get the images and mark them as permanent!
	// Because we record the graphics operations, the indicated
	// images must be permanently located at the same address
	// in order for the recorder to play back properly!
	//-------------------------------------------------------------
	PixelMap8
		*image = warehouse->pixelMap8Bin.Get(image_name);
	if (image != NULL)
	{
		Check(image);

		offset->x = image->Data.Size.x >> 1;
		offset->y = image->Data.Size.y >> 1;
	}

	Check_Fpu();
}

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// *DANGER*       HEAVY-DUTY WARNING      *DANGER*
//
// This object records graphics operations for
// later playback!
//
// Any graphic entities (such as bitmaps or gaugeImages)
// that are used here MUST be locked at the SAME address
// at playback time or the playback will crash HARD.
//
// *DANGER      CONSIDER YOURSELF WARNED   *DANGER*
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Compass::Compass(
	GaugeRate rate,
	ModeMask mode_mask,
	L4GaugeRenderer *new_renderer,
	unsigned int owner_ID,
	int graphics_port_number,

	int center_x, int center_y,
	int new_radius,
	int background_color,
	const char *north_map_name,
	const char *west_map_name,
	const char *south_map_name,
	const char *east_map_name,
	const char *bug_map_name,
	const char *identification_string
):
	GraphicGauge(
		rate,
		mode_mask,
		new_renderer,
		owner_ID,
		graphics_port_number,
		identification_string
	)
{
	Check_Pointer(this);

	// graphics_port may be NULL!

	localView.SetOrigin(center_x, center_y);

	radius = new_radius;
	backgroundColor = background_color;
	renderer = new_renderer;
	Check(renderer);

	//-------------------------------------------------
	// Make local copies of image names
	//-------------------------------------------------
	imageName[northIndex]= nameCopy(north_map_name);
	imageName[westIndex] = nameCopy(west_map_name);
	imageName[southIndex]= nameCopy(south_map_name);
	imageName[eastIndex] = nameCopy(east_map_name);
	imageName[bugIndex]  = nameCopy(bug_map_name);

	L4Warehouse
		*warehouse = (L4Warehouse *) renderer->warehousePointer;
	Check(warehouse);

	prepCompassMap(warehouse, north_map_name, &offset[northIndex]);
	prepCompassMap(warehouse, west_map_name,  &offset[westIndex]);
	prepCompassMap(warehouse, south_map_name, &offset[southIndex]);
	prepCompassMap(warehouse, east_map_name,  &offset[eastIndex]);
	prepCompassMap(warehouse, bug_map_name,   &offset[bugIndex]);

	Check_Fpu();
}

Compass::~Compass()
{
	Check(this);

	L4Warehouse
		*warehouse = (L4Warehouse *) renderer->warehousePointer;
	Check(warehouse);
	//-------------------------------------------------
	// Delete local copies of image names
	//-------------------------------------------------
	for(int i=0; i<numberOfItems; ++i)
	{
		warehouse->pixelMap8Bin.Release(imageName[i]);

		Unregister_Pointer(imageName[i]);
		delete[] imageName[i];
		imageName[i] = NULL;
	}

	Check_Fpu();
}

Logical
	Compass::TestInstance() const
{
	return GraphicGauge::TestInstance();
}

void
	Compass::ShowInstance(char *indent)
{
	Check(this);

	std::cout << indent << "Compass:\n";

	char
		temp[80];

	Str_Copy(temp,indent, 80);
	Str_Cat(temp,"...", 80);

//	std::cout << temp << "backgroundColor=" << backgroundColor << "\n";
//	std::cout << temp << "foregroundColor=" << foregroundColor << "\n";
//	std::cout << temp << "numberOfDigits =" << numberOfDigits << "\n";
	GraphicGauge::ShowInstance(temp);
	Check_Fpu();
}

void
	Compass::BecameActive()
{
	Check(this);

	previousPosition.x = -999.0;
	previousPosition.y = -999.0;
}

void
	Compass::Execute()
{
	Check(this);

	int
		i;

	Logical
		bug_is_blanked;

	Entity
		*viewing_entity = renderer->GetLinkedEntity();
	if (viewing_entity != NULL)
	{
		Check(viewing_entity);

		YawPitchRoll
			currentHeading;
		currentHeading = viewing_entity->localOrigin.angularPosition;

		SinCosPair
			sc_pair;
		sc_pair = currentHeading.yaw.angle; 

		Vector2DOf<int>
			north_vector;
		north_vector.x  = (int) (radius*sc_pair.sine + .5);
		north_vector.y  = (int) (radius*sc_pair.cosine + .5);

		if (
				(north_vector.x != previousPosition.x) ||
				(north_vector.y != previousPosition.y)
			)
		{
			previousPosition.x = north_vector.x;
			previousPosition.y = north_vector.y;

			Vector2DOf<int>
				position[numberOfItems];
			//---------------------------------------------------------
			// Get new compass positions
			//---------------------------------------------------------
			position[northIndex]   =  north_vector;

			position[eastIndex].x  =  north_vector.y;
			position[eastIndex].y  = -north_vector.x;

			position[southIndex].x = -north_vector.x;
			position[southIndex].y = -north_vector.y;

			position[westIndex].x  = -north_vector.y;
			position[westIndex].y  =  north_vector.x;
			//---------------------------------------------------------
			// Get new bug position
			//---------------------------------------------------------
			bug_is_blanked = True;

			RPPlayer
				*bugged_player = (RPPlayer *) viewing_entity->GetPlayerLink();

			if (bugged_player != NULL)
			{
				Check(bugged_player);
				Entity
					*bug_entity = bugged_player->goalEntity;

				if (bug_entity != NULL)
				{
					Check(bug_entity);

					if (imageName[bugIndex] != NULL)
					{
						Vector3D
							delta;
						delta.Subtract(
							bug_entity->localOrigin.linearPosition,
							viewing_entity->localOrigin.linearPosition
						);

						Scalar
							bug_yaw = generateRelativeYaw(
								currentHeading.yaw.angle,
								delta
							);

						sc_pair = bug_yaw;

						position[bugIndex].x =  (int) (radius*sc_pair.sine + .5);
						position[bugIndex].y = -(int) (radius*sc_pair.cosine + .5);

						bug_is_blanked = False;
					}
				}
			}
			//---------------------------------------------------------
			// Erase old compass
			//---------------------------------------------------------
			previousDrawing.Draw(&localView,backgroundColor);
			previousDrawing.Clear();
			//---------------------------------------------------------
			// Draw new compass
			//---------------------------------------------------------
			Check(renderer);
			L4Warehouse
				*warehouse = (L4Warehouse *) renderer->warehousePointer;
			Check(warehouse);

			localView.AttachRecorder(&previousDrawing);
			for(i=0; i<numberOfItems; ++i)
			{
				//---------------------------------------------------
				// Get, lock pixel map from warehouse
				//---------------------------------------------------
				PixelMap8
					*icon = warehouse->pixelMap8Bin.Get(imageName[i]);
				//---------------------------------------------------
				// Draw the icon
				//---------------------------------------------------
				if (icon != NULL)
				{
					if (bug_is_blanked && (i == bugIndex))
					{
						continue;
					}

					position[i].x -= offset[i].x;
					position[i].y -= offset[i].y;

					localView.MoveToAbsolute(
						position[i].x,
						position[i].y
					);

					localView.DrawPixelMap8(
						False,
						0,
						icon
					);
				}
				//---------------------------------------------------
				// Release pixel map from warehouse
				//---------------------------------------------------
				warehouse->pixelMap8Bin.Release(imageName[i]);
			}
			localView.DetachRecorder();
		}
	}
	Check_Fpu();
}

//*****************************************************************************
// ThreatIndicator
//*****************************************************************************
MethodDescription
	ThreatIndicator::methodDescription =
		{
			"threat",
			ThreatIndicator::Make,
			{
				{ ParameterDescription::typeRate, NULL },		// rate ID
				{ ParameterDescription::typeModeMask, NULL },// mode mask
				{ ParameterDescription::typeInteger, NULL },	// inner radius
				{ ParameterDescription::typeInteger, NULL },	// outer radius
				{ ParameterDescription::typeColor, NULL },	// background
				{ ParameterDescription::typeColor, NULL },	// teamcolor
				{ ParameterDescription::typeColor, NULL },	// othercolor
				{ ParameterDescription::typeScalar, NULL },	// view radius
				PARAMETER_DESCRIPTION_END
			}
		};

Logical
	ThreatIndicator::Make(
		int display_port_index,
		Vector2DOf<int> position,
		Entity */*entity*/,
		GaugeRenderer *gauge_renderer
	)
{
	Test_Tell("ThreatIndicator::Make\n");
	Check(gauge_renderer);

	ParameterDescription
		*parameterList = methodDescription.parameterList;

#	if DEBUG_LEVEL > 0
	parameterList[0].CheckIt(ParameterDescription::typeRate);		// group ID
	parameterList[1].CheckIt(ParameterDescription::typeModeMask);	// mode mask
	parameterList[2].CheckIt(ParameterDescription::typeInteger);// inner radius
	parameterList[3].CheckIt(ParameterDescription::typeInteger);// outer radius
	parameterList[4].CheckIt(ParameterDescription::typeColor);	// background
	parameterList[5].CheckIt(ParameterDescription::typeColor);	// team color
	parameterList[6].CheckIt(ParameterDescription::typeColor);	// other color
	parameterList[7].CheckIt(ParameterDescription::typeScalar);	// view radius
#	endif

#	if DEBUG_LEVEL > 0
	ThreatIndicator
		*threat_indicator =
#	endif
			new ThreatIndicator(
				parameterList[0].data.rate,
				parameterList[1].data.modeMask,
				(L4GaugeRenderer *) gauge_renderer,
				0, 										//unsigned int owner_ID
				display_port_index, 					//int graphics_port_number
				position.x, position.y,
				parameterList[2].data.integer,	//inner radius
				parameterList[3].data.integer,	//outer radius
				parameterList[4].data.color,		//bg color
				parameterList[5].data.color,		//team color
				parameterList[6].data.color,		//other color
				parameterList[7].data.scalar		//view radius
			);

	Register_Object(threat_indicator);

	Test_Tell("ThreatIndicator::Make OK\n");
	Check_Fpu();
	return True;
}

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// *DANGER*       HEAVY-DUTY WARNING      *DANGER*
//
// This object records graphics operations for
// later playback!
//
// Any graphic entities (such as bitmaps or gaugeImages)
// that are used here MUST be locked at the SAME address
// at playback time or the playback will crash HARD.
//
// *DANGER      CONSIDER YOURSELF WARNED   *DANGER*
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

ThreatIndicator::ThreatIndicator(
	GaugeRate rate,
	ModeMask mode_mask,
	L4GaugeRenderer *renderer,
	unsigned int owner_ID,
	int graphics_port_number,

	int center_x, int center_y,
	int inner_radius,
	int outer_radius,
	int bg_color,
	int team_color,
	int other_color,
	Scalar sensing_radius,
	const char *identification_string
):
	GraphicGauge(
		rate,
		mode_mask,
		renderer,
		owner_ID,
		graphics_port_number,
		identification_string
	)
{
	Check_Pointer(this);

	// graphics_port may be NULL!

	localView.SetOrigin(center_x, center_y);

	viewRadius = sensing_radius;
	topClosingVelocity = 75.0;						// in KPH, arbitrary choice	

	innerRadius     = inner_radius;
	outerRadius     = outer_radius;

	backgroundColor = bg_color;
	teamColor       = team_color;
	otherColor      = other_color;

	operatingPhase = 0;
	Check_Fpu();
}

ThreatIndicator::~ThreatIndicator()
{
	Check(this);

	// base class deletes all connections in Gauge::~Gauge
	Check_Fpu();
}

Logical
	ThreatIndicator::TestInstance() const
{
	return GraphicGauge::TestInstance();
}

void
	ThreatIndicator::ShowInstance(char *indent)
{
	Check(this);

	std::cout << indent << "ThreatIndicator:\n";

	char
		temp[80];

	Str_Copy(temp,indent, 80);
	Str_Cat(temp,"...", 80);

//	std::cout << temp << "backgroundColor=" << backgroundColor << "\n";
//	std::cout << temp << "foregroundColor=" << foregroundColor << "\n";
//	std::cout << temp << "numberOfDigits =" << numberOfDigits << "\n";
	GraphicGauge::ShowInstance(temp);
	Check_Fpu();
}

void
	ThreatIndicator::BecameActive()
{
	Check(this);
	// This empty method is required to override GaugeBase::BecameActive(),
	// which immediately complains and deactivates the gauge.
	Check_Fpu();
}

void
	ThreatIndicator::Execute()
{
	Check(this);

	switch(operatingPhase++)
	{
	case 0:
		Select();
		break;

	case 1:
		//-----------------------------------------------------------
		// Erase old threat display
		//-----------------------------------------------------------
		previousDrawing.Draw(&localView, backgroundColor);
		previousDrawing.Clear();
		//-----------------------------------------------------------
		// Draw new threat display
		//-----------------------------------------------------------
		localView.AttachRecorder(&previousDrawing);
		Draw();
		localView.DetachRecorder();
		// deliberately falls through into default case

	default:
		operatingPhase = 0;
		break;
	}
	Check_Fpu();
}

void
	ThreatIndicator::Select()
{
	Test_Tell("ThreatIndicator::Select\n");
	Check(this);

	Point3D
		viewing_position;

	Entity
		*viewing_entity = renderer->GetLinkedEntity();
	Check(viewing_entity);

	viewing_position = viewing_entity->localOrigin.linearPosition;

	movingEntityList.Clear();
	renderer->GetMovingEntitiesWithinBounds(
		&movingEntityList,
		viewing_position.x-viewRadius, -32768.0, viewing_position.z-viewRadius,
		viewing_position.x+viewRadius,  32768.0, viewing_position.z+viewRadius
	);
	Check_Fpu();
}

void
	ThreatIndicator::Draw()
{
	Test_Tell("ThreatIndicator::Draw\n");
	Check(this);

	Entity
		*entity,
		*viewing_entity = renderer->GetLinkedEntity();
	Check(viewing_entity);

	YawPitchRoll
		currentHeading;

	ChainIteratorOf<Entity*>
		i(movingEntityList.GetInstanceList());

	Vector3D
		my_velocity = ((Mover *)viewing_entity)->worldLinearVelocity,
		my_position = viewing_entity->localOrigin.linearPosition,
		relative_position,
		relative_velocity,
		unit_position;

	Scalar
		// HACK - calculate delta_radius at initialization instead!
		delta_radius = innerRadius - outerRadius,
		speed_factor,
		distance,
		distance_factor,
		dot_product,
		threat_yaw,
		threat_assessment;

	int
		color;

	//-------------------------------------------------------
	// Get player (for team ID if martian football)
	//-------------------------------------------------------
	RPPlayer
		*player = (RPPlayer *) viewing_entity->GetPlayerLink();
	Check(player);
#	if DEBUG_LEVEL == 0
		if (player == NULL)
		{
			DEBUG_STREAM << "ThreatIndicator has been SABOTAGED!\n" << std::flush;
			DEBUG_STREAM << "player = NULL!\n" << std::flush;
			return;
		}
#	endif
	//-------------------------------------------------------
	// Get current heading
	//-------------------------------------------------------
	currentHeading = viewing_entity->localOrigin.angularPosition;

	//-------------------------------------------------------
	// Attempt to display all entities
	//-------------------------------------------------------
	while ((entity=i.ReadAndNext()) != NULL)
	{
		Check(entity);
#		if DEBUG_LEVEL == 0
			if (entity == NULL)
			{
				DEBUG_STREAM << "ThreatIndicator has been SABOTAGED!\n" << std::flush;
				DEBUG_STREAM << "entity = NULL!\n" << std::flush;
				break;
			}
#		endif

		//-------------------------------------------------------
		// Make sure it's a VTV
		//-------------------------------------------------------
		if (! entity->IsDerivedFrom(*VTV::GetClassDerivations()))
		{
			continue;
		}

		//-------------------------------------------------------
		// Show all VTV's except us
		//-------------------------------------------------------
		if (entity != viewing_entity)
		{
			//-----------------------------------------------------------
			// Generate relative position
			//-----------------------------------------------------------
			relative_position.Subtract(
				entity->localOrigin.linearPosition,
				viewing_entity->localOrigin.linearPosition
			);
			//-----------------------------------------------------------
			// Generate distance, discard if too distant
			//-----------------------------------------------------------
			distance = relative_position.Length(); 
			if (distance < viewRadius)
			{
				//-----------------------------------------------------------
				// Generate threat assessment
				//-----------------------------------------------------------
				threat_assessment = 0.0;
				//-----------------------------------------
				// Assessment source #1: too close
				// (anything too close is dangerous)
				//-----------------------------------------
#				define CLOSE_THREAT_RADIUS 200.0

				if (distance <= CLOSE_THREAT_RADIUS)
				{
					threat_assessment +=
						(CLOSE_THREAT_RADIUS-distance)/CLOSE_THREAT_RADIUS;
				}
				//-----------------------------------------
				// Assessment source #1: closing with us
				// (anything closing with us is dangerous)
				//-----------------------------------------
				//--------------------------------
				// Normalize the position, but
				// make sure that we handle a
				// zero-length vector correctly.
				//--------------------------------
				if (Small_Enough(distance))
				{
					unit_position.x = 0.0;
					unit_position.y = 0.0;
					unit_position.z = -1.0;
				}
				else
				{
					unit_position = relative_position;
					unit_position /= distance;
					Check_Fpu();
				}

				//--------------------------------
				// Generate relative velocity
				// (TOWARDS the threat)
				//--------------------------------
				relative_velocity.Subtract(
					((Mover *) entity)->worldLinearVelocity,
					my_velocity
				);

				dot_product = -(unit_position * relative_velocity);
				if (dot_product > 0.0)
				{
					//----------------------------------
					// Faster is greather threat
					// Closer is greater threat
					//
					// Note that the speed_factor
					// is allowed to go above 1.0.
					// This allows distant, very
					// fast objects to be shown
					// as serious threats.
					// topClosingVelocity set in creator
					//----------------------------------
# if DEBUG_LEVEL == 0
	if (Small_Enough(topClosingVelocity))
	{
		DEBUG_STREAM << "ThreatIndicator has been SABOTAGED!\n" << std::flush;
		DEBUG_STREAM << "topClosingVelocity = " << topClosingVelocity << "\n" << std::flush;
	}
# endif
					Verify(!Small_Enough(topClosingVelocity)); 
					speed_factor = dot_product/topClosingVelocity;
					Check_Fpu();

# if DEBUG_LEVEL == 0
	if (Small_Enough(viewRadius))
	{
		DEBUG_STREAM << "ThreatIndicator has been SABOTAGED!\n" << std::flush;
		DEBUG_STREAM << "viewRadius = " << viewRadius << "\n" << std::flush;
	}
# endif
					Verify(!Small_Enough(viewRadius)); 
					distance_factor = 1.0 - (distance/viewRadius);
					Check_Fpu();

					threat_assessment +=
						distance_factor * speed_factor;
				}

				//-----------------------------------------------------------
				// Display only if assessment > 0
				//-----------------------------------------------------------
				if (threat_assessment > 0.0)
				{
					//-----------------------------------------------------------
					// Draw the threat indicator
					//-----------------------------------------------------------
					threat_yaw = generateRelativeYaw(
						currentHeading.yaw.angle,
						relative_position
					);

					SinCosPair
						sc_pair;
					sc_pair = -threat_yaw;
					// HACK - I don't know why the (-) is needed, but it is.
					// cpb 8-21-95
					Check_Fpu();

					if (threat_assessment > 1.0)
					{
						threat_assessment = 1.0;
					}
					Scalar
						show_radius  = outerRadius+(delta_radius*threat_assessment),

					orthogonal_x   =  sc_pair.cosine * 3.0,
					orthogonal_y   = -sc_pair.sine * 3.0,

					threat_outer_x =  sc_pair.sine * -outerRadius,
					threat_outer_y =  sc_pair.cosine * -outerRadius,

					threat_inner_x =  sc_pair.sine * -show_radius,
					threat_inner_y =  sc_pair.cosine * -show_radius;
					Check_Fpu();

					if (player->playerType == RPPlayer::GeneralPlayerType)
					{
						color = otherColor; // no friends in death race!
					}
					else
					{
						RPPlayer
							*other_player = (RPPlayer *) entity->GetPlayerLink();

						Check(other_player);
#						if DEBUG_LEVEL == 0
							if (other_player == NULL)
							{
								DEBUG_STREAM << "ThreatIndicator has been SABOTAGED!\n" << std::flush;
								DEBUG_STREAM << "other_player = NULL!\n" << std::flush;
								break;
							}
#						endif

						if (other_player->teamID == player->teamID)
						{
							color = teamColor;
						}
						else
						{
							color = otherColor;
						}
					}
					localView.SetColor(color);

					localView.MoveToAbsolute(
						(int) (threat_outer_x + orthogonal_x + .5),
						(int) (threat_outer_y + orthogonal_y + .5)
					);
					localView.DrawLineToAbsolute(
						(int) (threat_inner_x + .5),
						(int) (threat_inner_y + .5)
					);
					localView.DrawLineToAbsolute(
						(int) (threat_outer_x - orthogonal_x + .5),
						(int) (threat_outer_y - orthogonal_y + .5)
					);
				}
			}
		}
	}
	Check_Fpu();
}

//*****************************************************************************
// NavName
//*****************************************************************************
//----------------------------------------------------------
// A vector between each pair of objects is created,
// normalized, and then weighted according to the INVERSE
// of the vector length.  This will cause closer pairs
// of objects to generate larger "repelling" vectors, and
// farther pairs of objects to generate smaller " repelling"
// vectors.
//
// These 'repelling" vectors are then summed for each object.
// The summed vectors then tend to point AWAY from
// 'nearer' objects.  These summed vectors are
// again normalized and multiplied by a fixed distance to
// generate the center position for a name bitmap.
//----------------------------------------------------------

void
	NavName::ExtractFromEntity(Entity *entity, AffineMatrix &worldToView)
{
	Check(this);
	Check(entity);

	Player
		*player;
	//-------------------------------------
	// Get pointer to bitmap
	//-------------------------------------
	player = entity->playerLink;
	Check(application);
	Mission *current_mission = application->GetCurrentMission();
	Check(current_mission);

	if (player == NULL)
	{
		nameBitmap = NULL;
	}
	else
	{
		nameBitmap =
			current_mission->GetSmallNameBitmap(player->playerBitmapIndex);
		Check(nameBitmap);
	}
	//-------------------------------------
	// Generate screen position
	//-------------------------------------
	center.Multiply(
		entity->localOrigin.linearPosition,
		worldToView
	);
	//-------------------------------------
	// Get color
	//-------------------------------------
	color = determineEntityColor(entity);
	//-------------------------------------
	// Clear direction vector
	//-------------------------------------
	direction.x = 0.0;
	direction.y = 0.0;
	direction.z = 0.0;
}

void
	NavName::CalculatePositions(NavName *array, int count)
{
	Check_Pointer(array);

	int
		i;
	NavName
		*this_nav_name;
	Vector3D
		delta;
#if 0			// QUICK PATCH TO REMOVE "FLOATING" NAMES
	int
		i;
	NavName
		*that_nav_name;
	Scalar
		distance;
#endif
	//----------------------------------------------------------
	// Build NavName weighted direction vectors
	//----------------------------------------------------------
	for(
			i=0, this_nav_name=array;
			i<count;
			++i,++this_nav_name
		)
	{
		Check(this_nav_name);
#if 0			// QUICK PATCH TO REMOVE "FLOATING" NAMES
		for(
				j=i+1, that_nav_name=&array[j];
				j<count;
				++j,++that_nav_name
			)
		{
			//-------------------------------------
			// Get delta, length
			//-------------------------------------
			delta.Subtract(this_nav_name->center, that_nav_name->center);

			distance = delta.Length();
			//-------------------------------------
			// Normalize the delta, and
			// multiply by inverse distance
			// (i.e., divide by distance squared)
			//-------------------------------------
			distance *= distance;

			if (!Small_Enough(distance))    
			{
				delta /= distance;
			}
			//-------------------------------------
			// Sum the vectors for each navName
			//-------------------------------------
			this_nav_name->direction += delta;
			that_nav_name->direction -= delta;
		}
#else
		this_nav_name->direction.x = -1.0;
		this_nav_name->direction.y = 0.0;
		this_nav_name->direction.z = 0.0;
#endif
	}
#if 0			// QUICK PATCH TO REMOVE "FLOATING" NAMES
	//----------------------------------------------------------
	// Normalize the directions
	//----------------------------------------------------------
	for(
			i=0, this_nav_name=array;
			i<count;
			++i,++this_nav_name
		)
	{
		Check(this_nav_name);
		//--------------------------------------
		// Normalize the vectors in the xz plane
		//--------------------------------------
		this_nav_name->direction.y = 0.0;		// force to xz plane
		distance = this_nav_name->direction.Length();

		if (Small_Enough(distance))
		{
			this_nav_name->direction.x = -1.0;
			this_nav_name->direction.z = 0.0;
		}
			else
		{
			this_nav_name->direction /= distance;
		}
	}
#endif
}

void
	NavName::Draw(GraphicsView *graphics_view, Scalar radius_multiplier)
{
	Check(this);
	Check(graphics_view);

	if (nameBitmap != NULL)
	{
		//----------------------------------------
		// Set 'radius'
		//----------------------------------------
		direction *= (nameBitmap->Data.Size.x >> 1) * radius_multiplier;
		//----------------------------------------
		// Change from screen to world coordinates
		//----------------------------------------
		direction.y = -direction.z;
		center.y = -center.z;
		//----------------------------------------
		// Center the bitmap
		//----------------------------------------
		direction.x -= nameBitmap->Data.Size.x >> 1;
		direction.y -= nameBitmap->Data.Size.y >> 1;
		//----------------------------------------
		// Finally, draw it!
		//----------------------------------------
		graphics_view->MoveToAbsolute(
			(int) (center.x + direction.x + .5),
			(int) (center.y + direction.y + .5)
		);

		graphics_view->SetColor(color);
		graphics_view->DrawBitMap(0, nameBitmap);
	}
}

//*****************************************************************************
// NavDisplay
//*****************************************************************************
MethodDescription
	NavDisplay::methodDescription =
		{
			"nav",
			NavDisplay::Make,
			{
				{ ParameterDescription::typeRate, NULL },			// rate ID
				{ ParameterDescription::typeModeMask, NULL },	// mode mask
				{ ParameterDescription::typeVector, NULL },		// size
				{ ParameterDescription::typeColor, NULL },		// background
				{ ParameterDescription::typeColor, NULL },		// static
				{ ParameterDescription::typeAttribute, NULL },	// scale
				{ ParameterDescription::typeAttribute, NULL },	// lin pos
				{ ParameterDescription::typeAttribute, NULL },	// ang pos
				PARAMETER_DESCRIPTION_END
			}
		};

Logical
	NavDisplay::Make(
		int display_port_index,
		Vector2DOf<int> position,
		Entity */*entity*/,
		GaugeRenderer *gauge_renderer
	)
{
	Test_Tell("NavDisplay::Make\n");
	Check(gauge_renderer);

	ParameterDescription
		*parameterList = methodDescription.parameterList;

#	if DEBUG_LEVEL > 0
	parameterList[0].CheckIt(ParameterDescription::typeRate);		// group ID
	parameterList[1].CheckIt(ParameterDescription::typeModeMask);	// mode mask
	parameterList[2].CheckIt(ParameterDescription::typeVector);		// size
	parameterList[3].CheckIt(ParameterDescription::typeColor);		// bg
	parameterList[4].CheckIt(ParameterDescription::typeColor);		// bg
	parameterList[5].CheckIt(ParameterDescription::typeAttribute);	// scale
	parameterList[6].CheckIt(ParameterDescription::typeAttribute);	// lin pos
	parameterList[7].CheckIt(ParameterDescription::typeAttribute);	// ang pos
#	endif

#	if DEBUG_LEVEL > 0
	NavDisplay
		*nav_display =
#	endif
			new NavDisplay(
				parameterList[0].data.rate,
				parameterList[1].data.modeMask,
				(L4GaugeRenderer *) gauge_renderer,
				0, 													// owner_ID
				display_port_index, 								// graphics_port_number
				position.x, 
				position.y,
				position.x + parameterList[2].data.vector.x, 
				position.y + parameterList[2].data.vector.y,
				parameterList[3].data.color,					// bg
				parameterList[4].data.color,					// static objects
				(Scalar *) parameterList[5].data.attributePointer,
				(Point3D **) parameterList[6].data.attributePointer,
				(Quaternion **) parameterList[7].data.attributePointer,
				False

				// HACK!!!! The casts above are VERY dangerous!
			);

	Register_Object(nav_display);

	Test_Tell("NavDisplay::Make OK\n");
	Check_Fpu();
	return True;
}

NavDisplay::NavDisplay(
	GaugeRate rate,
	ModeMask mode_mask,
	L4GaugeRenderer *renderer,
	unsigned int owner_ID,
	int graphics_port_number,

	int left, int bottom,
	int right, int top,
	int bg_color,
	int static_color,
	Scalar *scale_value_pointer,
	Point3D **position_pointer,
	Quaternion **angle_pointer,
	Logical allow_rock_and_roll,
	const char *identification_string
):
	GraphicGauge(
		rate,
		mode_mask,
		renderer,
		owner_ID,
		graphics_port_number,
		identification_string
	)
{
	Test_Tell("NavDisplay::NavDisplay\n");
	Check_Pointer(this);

	// graphics_port may be NULL!

	//
	//-----------------------------------------------------------
	// Set position of graphicsView within the graphicsPort
	// (which also specifies the clipping and origin)
	//-----------------------------------------------------------
	//
	Verify(right > left);
	Verify(top > bottom);
	localView.SetPositionWithinPort(left, bottom, right, top);
	localView.SetOrigin((right-left)>>1, (top-bottom)>>1);
	//
	//-----------------------------------------------------------
	// Initialize internal values
	//-----------------------------------------------------------
	//
	backgroundColor = bg_color;
	staticColor = static_color;

	rockAndRoll = allow_rock_and_roll;

	halfWidth = (right-left)>>1; 
	halfHeight = (top-bottom)>>1;

	//
	//-----------------------------------------------------------
	// Build connections to values
	//-----------------------------------------------------------
	//
	GaugeConnection
		*connection;

	connection = new
		GaugeConnectionDirectOf<Scalar>(0, &currentScale, scale_value_pointer);
	Register_Object(connection);
	AddConnection(connection);

	connection = new
		GaugeConnectionDirectOf<Point3D *>(
			0, &currentPositionPointer, position_pointer);
	Register_Object(connection);
	AddConnection(connection);

	connection = new
		GaugeConnectionDirectOf<Quaternion *>(
			0, &currentAngularPointer, angle_pointer);
	Register_Object(connection);
	AddConnection(connection);

	Check_Fpu();
}

NavDisplay::~NavDisplay()
{
	Test_Tell("NavDisplay::~NavDisplay\n");
	Check(this);

	// base class deletes all connections in Gauge::~Gauge
	Check_Fpu();
}

Logical
	NavDisplay::TestInstance() const
{
	return GraphicGauge::TestInstance();
}

void
	NavDisplay::ShowInstance(char *indent)
{
	Check(this);

	std::cout << indent << "ThreatIndicator:\n";

	char
		temp[80];

	Str_Copy(temp,indent, 80);
	Str_Cat(temp,"...", 80);

//	std::cout << temp << "backgroundColor=" << backgroundColor << "\n";
//	std::cout << temp << "foregroundColor=" << foregroundColor << "\n";
//	std::cout << temp << "numberOfDigits =" << numberOfDigits << "\n";
	GraphicGauge::ShowInstance(temp);
	Check_Fpu();
}

void
	NavDisplay::BecameActive()
{
	Check(this);

	operatingPhase = 0;
}

void
	NavDisplay::Execute()
{
	Test_Tell("NavDisplay::Execute\n");
	Check(this);
	Check(renderer);

	Vector3D
		scaling_vector;
	Entity
		*linked_entity;
	LinearMatrix
		temp;

	switch(operatingPhase++)
	{
	case 0:
		CalculateBounds();
		break;

	case 1:
		SelectStatic();
		break;

	case 2:
		SelectMoving();
		break;

	case 3:
		temp.BuildIdentity();
		//-----------------------------------------------------------
		// Update worldToView transformation
		//-----------------------------------------------------------
		linked_entity = renderer->GetLinkedEntity();
		//------------------------------------
		// If pointer to position is null,
		// use position of linked entity
		//------------------------------------
		if (currentPositionPointer == NULL)
		{
			if (linked_entity != NULL)
			{
				currentPositionPointer =
					&linked_entity->localOrigin.linearPosition;
			}
		}
		//------------------------------------
		// Get inverse transform from viewer
		//------------------------------------
		if (currentPositionPointer != NULL)
		{
			{
				Point3D
					point;
				point = *currentPositionPointer;
				temp = point;
			}
			if (currentAngularPointer != NULL)
			{
				if (rockAndRoll)
				{
					Quaternion
						quaternion;
					quaternion = *currentAngularPointer;

					temp = quaternion;
				}
				else
				{
					YawPitchRoll
						currentHeading;
					currentHeading = *currentAngularPointer;

					SinCosPair
						sin_cos_pair;
					sin_cos_pair = currentHeading.yaw.angle/2.0;

					Quaternion
						quaternion(0.0, sin_cos_pair.sine, 0.0, sin_cos_pair.cosine);

					temp = quaternion;
				}
			}
		}
		worldToView.Invert(temp);
		//------------------------------------
		// Scale the display
		//------------------------------------
		Verify(pixelsPerMeter > 0.0);
		scaling_vector.x = pixelsPerMeter; 
		scaling_vector.y = pixelsPerMeter; 
		scaling_vector.z = pixelsPerMeter;

		worldToView *= scaling_vector;
		//
		//-----------------------------------------------------------
		// Update display
		//-----------------------------------------------------------
		//
		previousDrawing.Draw(&localView, backgroundColor);
		previousDrawing.Clear();
		localView.AttachRecorder(&previousDrawing);
		DrawStatic(worldToView);
		DrawMoving(worldToView);
		DrawNames(worldToView);
		localView.DetachRecorder();

		// deliberately falls through into default case

	default:
		operatingPhase = 0;
		break;
	}
	Check_Fpu();
}

void
	NavDisplay::CalculateBounds()
{
	Test_Tell("NavDisplay::CalculateBounds\n");
	Check(this);

	Test_Tell("currentScale = " << currentScale << "\n");

	if (currentScale <= 0.0)
	{
		Fail("NavDisplay::CalculateBounds detected currentScale <= 0.0");
		currentScale = 1.0;
	}

	pixelsPerMeter = (halfWidth<<1)/currentScale;

	Test_Tell("pixelsPerMeter = " << pixelsPerMeter << "\n");
	Verify(pixelsPerMeter > 0.0);

	metersPerPixel = 1.0 / pixelsPerMeter;

	//------------------------------------------------------------
	// Derivation of LODIndex:
	//
	// The LOD level for the gaugeImage is a very arbitrary value
	// where 1.0 is the "normal" level of detail, with smaller
	// numbers yielding "more" detail and larger numbers yielding
	// "less" detail.  If the LODIndex becomes greater than the
	// largest value in the gaugeImage, the gaugeImage is not drawn.
	//
	// This code assumes that a "normal" level is N pixels per meter.
	// Therefore,
	//                  N pixel  |  meters
	//    LODIndex = ------------+----------
	//                   meter   |  pixel
	//------------------------------------------------------------
	LODIndex = /*1.0* */ metersPerPixel;		// N = 1.0 pixels/meter

	Scalar
		halfViewWidth(halfWidth/pixelsPerMeter),
		halfViewHeight(halfHeight/pixelsPerMeter);
	Test_Tell("halfViewWidth = " << halfViewWidth << "\n");
	Test_Tell("halfViewHeight = " << halfViewHeight << "\n");

	Point3D
		viewing_position;

	Entity
		*viewing_entity = renderer->GetLinkedEntity();
	Check(viewing_entity);

	viewing_position = viewing_entity->localOrigin.linearPosition;

	xMin = viewing_position.x - halfViewWidth;
	xMax = viewing_position.x + halfViewWidth;

	yMin = -32768.0;
	yMax =  32768.0;

	zMin = viewing_position.z - halfViewHeight;
	zMax = viewing_position.z + halfViewHeight;

	Test_Tell("x= " << xMin << "..." << xMax << "\n");
	Test_Tell("y= " << yMin << "..." << yMax << "\n");
	Test_Tell("z= " << zMin << "..." << zMax << "\n");
	Check_Fpu();
}

void
	NavDisplay::SelectStatic()
{
	Test_Tell("NavDisplay::SelectStatic\n");
	Check(this);

	staticEntityList.Clear();
	renderer->GetStaticEntitiesWithinBounds(
		&staticEntityList,
		xMin, yMin, zMin,
		xMax, yMax, zMax
	);
	Check_Fpu();
}

void
	NavDisplay::SelectMoving()
{
	Test_Tell("NavDisplay::SelectMoving\n");
	Check(this);

	movingEntityList.Clear();
	renderer->GetMovingEntitiesWithinBounds(
		&movingEntityList,
		xMin, yMin, zMin,
		xMax, yMax, zMax
	);
	Check_Fpu();
}

void
	NavDisplay::DrawStatic(AffineMatrix &worldToView)
{
	Test_Tell("NavDisplay::DrawStatic\n");
	Check(this);

	Entity
		*entity;
	Point3D
		zero,
		pos;
	AffineMatrix
		localToView;
	ChainIteratorOf<Entity*>
		i(staticEntityList.GetInstanceList());

	zero.x = 0.0; 
	zero.y = 0.0; 
	zero.z = 0.0; 

	Check(renderer);
	L4Warehouse
		*warehouse = (L4Warehouse *) renderer->warehousePointer;
	Check(warehouse);

	L4GaugeImage
		*gauge_image;

	while ((entity=i.ReadAndNext()) != NULL)
	{
		Check(entity);

		//
		//-----------------------------------------------------------
		// Draw new image
		//-----------------------------------------------------------
		//
		gauge_image = warehouse->
			gaugeImageBin.GetIfAlreadyExists(entity->GetResourceID());
		if (gauge_image != NULL)
		{
			Check(gauge_image);

			localToView.Multiply(entity->localToWorld, worldToView);

			gauge_image->Draw(
				LODIndex,
				metersPerPixel,
				&localView,
				staticColor,
				(AffineMatrix &) localToView
			);

			warehouse->gaugeImageBin.Release(entity->GetResourceID());
		}
	}
	Check_Fpu();
}

void
	NavDisplay::DrawMoving(AffineMatrix &worldToView)
{
	Test_Tell("NavDisplay::DrawMoving\n");
	Check(this);

	Entity
		*entity;
	ChainIteratorOf<Entity*>
		i(movingEntityList.GetInstanceList());
	L4GaugeImage
		*gauge_image;
	AffineMatrix
		localToView;
	//----------------------------------------------------------
	// Get warehouse
	//----------------------------------------------------------
	L4Warehouse
		*warehouse = (L4Warehouse *) renderer->warehousePointer;
	Check(warehouse);

	//----------------------------------------------------------
	// Draw moving entities
	//----------------------------------------------------------
	while ((entity=i.ReadAndNext()) != NULL)
	{
		Check(entity);

//		if (entity != renderer->GetLinkedEntity())
		{
			//
			//-----------------------------------------------------------
			// Draw new image
			//-----------------------------------------------------------
			//
			gauge_image = warehouse->
				gaugeImageBin.GetIfAlreadyExists(entity->GetResourceID());

			if (gauge_image != NULL)
			{
				Check(gauge_image);

				localToView.Multiply(entity->localToWorld, worldToView);

				gauge_image->Draw(
					LODIndex,
					metersPerPixel,
					&localView,
					determineEntityColor(entity),
					localToView,
					0					// bounding box color number: 0=no box
				);

				warehouse->gaugeImageBin.Release(entity->GetResourceID());
			}
		}
	}
	Check_Fpu();
}

void
	NavDisplay::DrawNames(AffineMatrix &worldToView)
{
	Test_Tell("NavDisplay::DrawNames\n");
	Check(this);

	ChainIteratorOf<Entity*>
		iterator(movingEntityList.GetInstanceList());
	NavName
		*nav_name;
	int
		i;
	Entity
		*entity;

	int
		count = iterator.GetSize();

	if (count < 1)
	{
		return;
	}

	if (count > maximumNames)
	{
		count = maximumNames;
	}
	//----------------------------------------------------------
	// Build NavName entries
	//----------------------------------------------------------
	for(
			i=0, nav_name=nameArray;
			i<count;
			++i,++nav_name
		)
	{
		//-------------------------------------
		// Get entity
		//-------------------------------------
		entity=iterator.ReadAndNext();
		if (entity == NULL)
		{
			Fail("NavDisplay::DrawNames - GetSize() lied!");
		}
		nav_name->ExtractFromEntity(entity, worldToView);
	}

	//----------------------------------------------------------
	// Generate display positions
	//----------------------------------------------------------
	NavName::CalculatePositions(nameArray, count);

	//----------------------------------------------------------
	// Draw names
	//----------------------------------------------------------
	for(
			i=0, nav_name=nameArray;
			i<count;
			++i,++nav_name
		)
	{
		nav_name->Draw(&localView, 1.0);
	}
	Check_Fpu();
}

//*****************************************************************************
// GPS
//*****************************************************************************
MethodDescription
	GPS::methodDescription =
		{
			"gps",
			GPS::Make,
			{
				{ ParameterDescription::typeRate, NULL },			// rate ID
				{ ParameterDescription::typeModeMask, NULL },	// mode mask
				{ ParameterDescription::typeVector, NULL },		// size
				{ ParameterDescription::typeScalar, NULL },		// LOD index
				PARAMETER_DESCRIPTION_END
			}
		};

Logical
	GPS::Make(
		int display_port_index,
		Vector2DOf<int> position,
		Entity */*entity*/,
		GaugeRenderer *gauge_renderer
	)
{
	Test_Tell("GPS::Make\n");
	Check(gauge_renderer);

	ParameterDescription
		*parameterList = methodDescription.parameterList;

#	if DEBUG_LEVEL > 0
	parameterList[0].CheckIt(ParameterDescription::typeRate);		// group ID
	parameterList[1].CheckIt(ParameterDescription::typeModeMask);	// mode mask
	parameterList[2].CheckIt(ParameterDescription::typeVector);		// size
	parameterList[3].CheckIt(ParameterDescription::typeScalar);		// LOD index
#	endif

#	if DEBUG_LEVEL > 0
	GPS
		*gps =
#	endif
			new GPS(
				parameterList[0].data.rate,
				parameterList[1].data.modeMask,
				(L4GaugeRenderer *) gauge_renderer,
				0, 										//unsigned int owner_ID
				display_port_index, 					//int graphics_port_number
				position.x, 
				position.y,
				position.x + parameterList[2].data.vector.x, 
				position.y + parameterList[2].data.vector.y,
				parameterList[3].data.scalar
			);

	Register_Object(gps);

	Test_Tell("GPS::Make OK\n");
	Check_Fpu();
	return True;
}

GPS::GPS(
	GaugeRate rate,
	ModeMask mode_mask,
	L4GaugeRenderer *renderer,
	unsigned int owner_ID,
	int graphics_port_number,
	int left, int bottom,
	int right, int top,
	Scalar LOD_index,
	const char *identification_string
) :
	GraphicGauge(
		rate,
		mode_mask,
		renderer,
		owner_ID,
		graphics_port_number,
		identification_string
	)
{
	Test_Tell("GPS::GPS()\n");
	Check_Pointer(this);

	// graphics_port may be NULL!

	width  = right-left,
	height = top-bottom;
	Verify(width > 0);
	Verify(height > 0);

	LODIndex = LOD_index;
	//-----------------------------------------------------------
	// Set position of graphicsView within the graphicsPort
	// (which also specifies the clipping and origin)
	//-----------------------------------------------------------
	localView.SetPositionWithinPort(left, bottom, right, top);
	//-----------------------------------------------------------
	// Create background pixelmap
	//-----------------------------------------------------------
	background = new Video8BitBuffered(width, height);
	Register_Object(background);
	needsStaticUpdate = True;
	Check_Fpu();
}

GPS::~GPS()
{
	Check(this);

	Unregister_Object(background);
	delete background;
   background = NULL;

	Check_Fpu();
}

Logical
	GPS::TestInstance() const
{
	return GraphicGauge::TestInstance();
}

void
	GPS::ShowInstance(char */*indent*/)
{
	Check_Fpu();
}

void
	GPS::BecameActive()
{
	Check(this);
	numberOfMovingEntities = 0;
	needsScreenUpdate = True;
	Check_Fpu();
}

void
	GPS::NotifyOfNewInterestingEntity(Entity *entity)
{
	Check(this);
	Check(entity);
	if (entity->IsDerivedFrom(*Terrain::GetClassDerivations()))
	{
		needsStaticUpdate = True;
	}
	Check_Fpu();
}

void
	GPS::UpdateStaticEntities()
{
	Check(this);
	//-----------------------------------------------------------
	// Calculate scale
	//-----------------------------------------------------------
	Scalar
		minX, maxX, deltaX,
		minY, maxY,
		minZ, maxZ, deltaZ,
		scaleX, scaleZ;

	//----------------------------------------
	// First, get bounds from staticEntityList
	//----------------------------------------
	Check(renderer);
	renderer->GetStaticBounds(
		&minX, &minY, &minZ,
		&maxX, &maxY, &maxZ
	);
	//----------------------------------------
	// Calculate X, Z scales
	//----------------------------------------
	deltaX = maxX - minX;
	if (deltaX < 0)
	{
		deltaX = - deltaX;
	}
	if (deltaX != 0)
	{
		Verify(width > 0);
		scaleX = deltaX/((Scalar) width);
	}
	else
	{
		scaleX = 1.0;
	}

	deltaZ = maxZ - minZ;
	if (deltaZ < 0)
	{
		deltaZ = - deltaZ;
	}
	if (deltaZ > 0)
	{
		Verify(height > 0);
		scaleZ = deltaZ/((Scalar) height);
	}
	else
	{
		scaleZ = 1.0;
	}
	//----------------------------------------
	// Choose largest scaling value
	// (such that entire map fits)
	//----------------------------------------
	metersPerPixel = (scaleX > scaleZ)? scaleX : scaleZ;
	metersPerPixel *= 1.2;		// allow for large objects at edges of map
	//----------------------------------------
	// Generate inverse as well
	//----------------------------------------
	Verify(!Small_Enough(metersPerPixel));
	pixelsPerMeter = 1.0 / metersPerPixel;

//DEBUG_STREAM <<
//	"GPS, dx=" << deltaX <<
//	", dz=" << deltaZ <<
//	", sx=" << scaleX <<
//	", sz=" << scaleZ <<
//	", metersPerPixel=" << metersPerPixel <<
//	", pixelsPerMeter=" << pixelsPerMeter <<
//	"\n";
	//----------------------------------------
	// Generate centering offset
	// (map is not necessarily symmetrical)
	//----------------------------------------
	centeringOffset.x = -(deltaX/2 + minX);
	centeringOffset.y = 0.0;
	centeringOffset.z = -(deltaZ/2 + minZ);
	//-----------------------------------------------------------
	// Rebuild background
	//-----------------------------------------------------------
	Check(background);
	L4BytePort
		backgroundPort(background, "background", 0);
	Check(&backgroundPort);

	GraphicsView
		backgroundView(&backgroundPort);
	Check(&backgroundView);

	backgroundView.MoveToAbsolute(0, 0);
	backgroundView.SetColor(0);
	backgroundView.DrawFilledRectangleToAbsolute(width,height);

	backgroundView.SetOrigin(width>>1, height>>1);

	GaugeEntityList
		staticEntityList;
	renderer->GetStaticEntities(&staticEntityList);

	ChainIteratorOf<Entity*>
		i(staticEntityList.GetInstanceList());

	Check(renderer);
	L4Warehouse
		*warehouse = (L4Warehouse *) renderer->warehousePointer;
	Check(warehouse);

	Entity
		*entity;
	L4GaugeImage
		*gauge_image;

	AffineMatrix
		worldToView,
		localToView;

	Vector3D
		scaling_vector;
	//------------------------------------
	// Scale the display
	//------------------------------------

	Verify(!Small_Enough(pixelsPerMeter));

	scaling_vector.x = pixelsPerMeter; 
	scaling_vector.y = pixelsPerMeter; 
	scaling_vector.z = pixelsPerMeter;

	worldToView.BuildIdentity();
	worldToView *= centeringOffset;	// translation
	worldToView *= scaling_vector;

	while ((entity=i.ReadAndNext()) != NULL)
	{
		Check(entity);
		//-------------------------------------
		// Draw image
		//-------------------------------------
		gauge_image = warehouse->
			gaugeImageBin.GetIfAlreadyExists(entity->GetResourceID());
		if (gauge_image != NULL)
		{
			Check(gauge_image);

			localToView.Multiply(entity->localToWorld, worldToView);

			gauge_image->Draw(
				LODIndex,			// value set by creator
				metersPerPixel,
				&backgroundView,
				0,						// default color
				(AffineMatrix &) localToView
			);

			warehouse->gaugeImageBin.Release(entity->GetResourceID());
		}
	}
	//-----------------------------------------------------------
	// Redraw new background, restart moving entity display
	//-----------------------------------------------------------
	BecameActive();
	Check_Fpu();
}


void
	GPS::Execute()
{
	Check(this);

	GaugeEntityList
		movingEntityList;
	GPSRecord
		*gps_record;
	int
		size,
		i;

	enum
	{
		my_size=3,
		your_size=2
	};

	//-----------------------------------------------------------
	// Recreate background if it has changed
	//-----------------------------------------------------------
	if (needsStaticUpdate)
	{
		UpdateStaticEntities();
		needsStaticUpdate = False;
	}
	//-----------------------------------------------------------
	// Redraw background if needed
	//-----------------------------------------------------------
	if (needsScreenUpdate)
	{
		Check(background);
		Check(background->pixelBuffer);

		localView.MoveToAbsolute(0, 0);
		localView.DrawPixelMap8(
			True,		// opaque
			0,			// rotation
			background->pixelBuffer
		);

		needsScreenUpdate = False;
	}
	//-----------------------------------------------------------
	// Erase old positions
	//-----------------------------------------------------------
	for(
			i=numberOfMovingEntities, gps_record=previousGPSRecord;
			i>0;
			--i, ++gps_record
		)
	{
		size = (gps_record->me)? my_size : your_size ;

		localView.MoveToAbsolute(gps_record->x, gps_record->y);
		localView.DrawPixelMap8(
			True,								// opaque
			0,									// rotation
			background->pixelBuffer,
			gps_record->x,					// sx1
			gps_record->y,					// sy1
			gps_record->x+size,			// sx2
			gps_record->y+size			// sy2
		);
	}
	//-----------------------------------------------------------
	// Get new entity list
	//-----------------------------------------------------------
	movingEntityList.Clear();
	renderer->GetMovingEntities(&movingEntityList);
	//-----------------------------------------------------------
	// Build new positions
	//-----------------------------------------------------------
	gps_record = previousGPSRecord;
	numberOfMovingEntities = 0;
	{
		ChainIteratorOf<Entity*>
			iterator(movingEntityList.GetInstanceList());
		Entity
			*entity;
		RPPlayer
			*player;
		int
      	half_width = width >> 1,
			half_height = height >> 1;

		while((entity=iterator.ReadAndNext()) != NULL)
		{
			Check(entity);
			//-----------------------------------------------------------
			// Only show entities representing players
			//-----------------------------------------------------------
			player = (RPPlayer *)(entity->GetPlayerLink());
			if (player != NULL)
			{
				Check(player);

				gps_record->x = (int)
					(
						(
							(entity->localOrigin.linearPosition.x+centeringOffset.x)
							* pixelsPerMeter
						)
						+ .5 + half_width
					);
				gps_record->y = (int)
					(
						(
							-(entity->localOrigin.linearPosition.z+centeringOffset.z)
							* pixelsPerMeter
						)
						+ .5 + half_height
					);

				gps_record->me = (entity == renderer->GetLinkedEntity());
				gps_record->color = determineEntityColor(entity);

				++gps_record;
				++numberOfMovingEntities;
				if (numberOfMovingEntities >= max_moving_entities)
				{
					break;
				}
			}
		}
	}
	//-----------------------------------------------------------
	// Draw new positions
	//-----------------------------------------------------------
	for(
			i=numberOfMovingEntities, gps_record=previousGPSRecord;
			i>0;
			--i, ++gps_record
		)
	{
		if (gps_record->me)
		{
			size = my_size;
			localView.SetColor(gps_record->color + 0xC0);	// flashes color
		}
		else
		{
			size = your_size;
			localView.SetColor(gps_record->color);
		}

		localView.MoveToAbsolute(gps_record->x, gps_record->y);
		localView.DrawFilledRectangleToRelative(size,size);
	}
	Check_Fpu();
}

//############################################################################
// Ranking
//############################################################################

#	define FORCE_CHANNEL_UPDATE -1

MethodDescription
	Ranking::methodDescription =
		{
			"ranking",
			Ranking::Make,
			{
				{ ParameterDescription::typeRate, NULL },			// rate ID
				{ ParameterDescription::typeModeMask, NULL },	// mode mask
				{ ParameterDescription::typeString, NULL },		// font name
				{ ParameterDescription::typeInteger, NULL },		// spacing
				{ ParameterDescription::typeColor, NULL },		// score color
				{ ParameterDescription::typeInteger, NULL },		// options
				PARAMETER_DESCRIPTION_END
			}
		};

Logical
	Ranking::Make(
		int display_port_index,
		Vector2DOf<int> position,
		Entity */*entity*/,
		GaugeRenderer * gauge_renderer
	)
{
	ParameterDescription
		*parameterList = methodDescription.parameterList;


#	if DEBUG_LEVEL > 0
	parameterList[0].CheckIt(ParameterDescription::typeRate);		// group ID
	parameterList[1].CheckIt(ParameterDescription::typeModeMask);	// mode mask
	parameterList[2].CheckIt(ParameterDescription::typeString);		// font name
	parameterList[3].CheckIt(ParameterDescription::typeInteger);	// spacing
	parameterList[4].CheckIt(ParameterDescription::typeColor);		// color
	parameterList[5].CheckIt(ParameterDescription::typeInteger);	// options
#	endif

	Test_Tell("Ranking::Make\n");

#	if DEBUG_LEVEL > 0
	Ranking
		*ranking =
#	endif
			new Ranking(
				parameterList[0].data.rate,
				parameterList[1].data.modeMask,
				(L4GaugeRenderer *) gauge_renderer,
				0, 										// unsigned int owner_ID
				display_port_index, 					// int graphics_port_number
				position.x, position.y,
				parameterList[2].data.string,		// font name
				parameterList[3].data.integer,	// vertical spacing
				parameterList[4].data.color,		// numeric color
				parameterList[5].data.integer		// options
			);

	Register_Object(ranking);

	//--------------------------------------------------------
	// Make sure the font was properly placed in the warehouse
	//--------------------------------------------------------
	Check(gauge_renderer);
	L4Warehouse
		*warehouse = (L4Warehouse *) gauge_renderer->warehousePointer;
	Check(warehouse);

	if (warehouse->bitMapBin.Get(parameterList[2].data.string) == NULL)
	{
		Test_Tell("Ranking::Missing font\n");
		Check_Fpu();
		return False;
	}
	else
	{
		warehouse->bitMapBin.Release(parameterList[2].data.string);
	}
	//--------------------------------------------------------
	// Everything's ok
	//--------------------------------------------------------
	Test_Tell("Ranking::Make OK\n");
	Check_Fpu();
	return True;
}

Ranking::Ranking(
	GaugeRate rate,
	ModeMask mode_mask,
	L4GaugeRenderer *renderer,
	unsigned int owner_ID,
	int graphics_port_number,
	int left, int top,
	const char *font_name,
	int new_spacing,
	int numeric_color,
	int new_options,
	const char *identification_string
):
	GraphicGauge(
		rate,
		mode_mask,
		renderer,
		owner_ID,
		graphics_port_number,
		identification_string
	)
{
	Test_Tell("Ranking creator\n" << std::flush);
	Check_Pointer(this);
	// graphics_port may be NULL!

	spacing = new_spacing;
	options = new_options;
	x = left;
	y = top - 32;
	//-----------------------------------------------------------
	// Get and lock font bitmap from warehouse
	//-----------------------------------------------------------
	Check(renderer);

	L4Warehouse
		*warehouse = (L4Warehouse *) renderer->warehousePointer;
	Check(warehouse);

	Check_Pointer(font_name);
	BitMap
		*font = warehouse->bitMapBin.Get(font_name);

	Check(font);
	//-----------------------------------------------------------
	// Size/offset definitions
	//-----------------------------------------------------------
#	define width_of_bitmap 			128
#	define space_before_score		2
#	define number_of_score_digits	8
#	define space_before_channel	32
#	define space_at_end_of_box		4

	int
		char_width = (font->Data.Size.x/NumericDisplay::totalDigitsPerFont);
	int
		score_width = char_width * number_of_score_digits;
	int
		channel_width = char_width;
	int
		score_offset = width_of_bitmap + space_before_score;
	int
		channel_offset = score_offset + score_width + space_before_channel;

	//-----------------------------------------------------------
	// Calculate box width
	//-----------------------------------------------------------
	width = width_of_bitmap + space_before_score +
		score_width + space_at_end_of_box;
	if (options & showIcomOption)
	{
		width += space_before_channel + channel_width;
	} 
	//-----------------------------------------------------------
	// Spawn off sub-instruments
	//-----------------------------------------------------------
	int
		i,
		bottom = y+3;
	for(i=0; i<maximumDisplayedPlayers; ++i,bottom -= spacing)
	{
		numericDisplay[i] = new NumericDisplay(
			warehouse,
			x+score_offset, bottom,
			font_name,
			number_of_score_digits,
			NumericDisplay::signedBlankedZerosFormat,
			0,
			numeric_color
		);
		Register_Object(numericDisplay[i]);

		if (options & showIcomOption)
		{
			channelDisplay[i] = new NumericDisplay(
				warehouse,
				x+channel_offset, bottom,
				font_name,
				1,
				NumericDisplay::unsignedFormat,
				0,
				numeric_color
			);
			Register_Object(channelDisplay[i]);
		}
		else
		{
			channelDisplay[i] = NULL;
		}
	}
	//-----------------------------------------------------------
	// Release the font bitmap from warehouse
	//-----------------------------------------------------------
	Check(warehouse);
	Check_Pointer(font_name);
	warehouse->bitMapBin.Release(font_name);

	Check_Fpu();
}

Ranking::~Ranking()
{
	Test_Tell("Ranking destructor\n" << std::flush);
	Check(this);

	int
		i;
	//
	//-----------------------------------------------------------
	// Delete sub-gauges
	//-----------------------------------------------------------
	//
	for(i=0; i<maximumDisplayedPlayers; ++i)
	{
		if (numericDisplay[i] != NULL)
		{
			Unregister_Object(numericDisplay[i]);
			delete numericDisplay[i];
			numericDisplay[i] = NULL;
		}
		if (channelDisplay[i] != NULL)
		{
			Unregister_Object(channelDisplay[i]);
			delete channelDisplay[i];
			channelDisplay[i] = NULL;
		}
	}
	Check_Fpu();
}


Logical
	Ranking::TestInstance() const
{
	return GraphicGauge::TestInstance();
}

void
	Ranking::ShowInstance(char */*indent*/)
{
	Check_Fpu();
}

void
	Ranking::LinkToEntity(Entity *entity)
{
	Test_Tell("Ranking::LinkToEntity\n" << std::flush);
	Check(this);

	linkedEntity = entity;
	Check_Fpu();
}

void
	Ranking::BecameActive()
{
	Check(this);

	previousRanking = 999;
	previousIntercomEnabled = False;
	//-------------------------------------------
	// Clear the current array
	//-------------------------------------------
	for(int i=0; i<maximumDisplayedPlayers; ++i)
	{
		previousArray[i] = (Player *) -1;
		previousScore[i] = -.001;
		previousChannel[i] = FORCE_CHANNEL_UPDATE;

		if (numericDisplay[i] != NULL)
		{
			Check(numericDisplay[i]);
			numericDisplay[i]->ForceUpdate();
		}
	}
}

void
	Ranking::Execute()
{
	Test_Tell("Ranking::Execute\n" << std::flush);
	Check(this);

	Check(application);
	Check(application->GetEntityManager());

	EntityGroup
		*all_players = application->GetEntityManager()->FindGroup("Players");

	if (all_players != NULL)
	{
		Check(all_players);

		Player
			*player,
			*currentArray[maximumDisplayedPlayers];
		int
			i,
			bottom,
			currentRanking;

		Logical
			intercomEnabled;

		//-------------------------------------------
		// Get 'our' placement & intercom status
		//-------------------------------------------
		if (linkedEntity == NULL)
		{
			currentRanking = -1;
			intercomEnabled = False;
		}
		else
		{
			Check(linkedEntity);
			player = linkedEntity->GetPlayerLink();
			Check(player);
			currentRanking = player->playerRanking;

			Icom
				*intercom = player->GetIntercom();
			Check(intercom);
			intercomEnabled = intercom->GetChannel() != Icom::undefinedChannel;
		}

		//-------------------------------------------
		// Clear the current array
		//-------------------------------------------
		for(i=0; i<maximumDisplayedPlayers; ++i)
		{
			currentArray[i] = NULL;
		}
		//-------------------------------------------
		// Build the current array
		//-------------------------------------------
		ChainIteratorOf<Node*>
			iterator(all_players->groupMembers);
		while((player=(Player *) iterator.ReadAndNext()) != NULL)
		{
			Check(player);
			if (player->IsScoringPlayer())
			{
				if (player->playerRanking < maximumDisplayedPlayers)
				{
					Verify(player->playerRanking >= 0);

// HACK! Safety code for OPT version
					if (player->playerRanking >= 0)
					{
						currentArray[player->playerRanking] = player;
					}
				}
			}
		}
		//----------------------------------------------------------------------
		// Compare to previousArray,
		// draw names if order has changed
		//----------------------------------------------------------------------
		bottom = y;
		for(i=0; i<maximumDisplayedPlayers; ++i,bottom-=spacing)
		{
			if (previousArray[i] != currentArray[i])
			{
				previousArray[i] = currentArray[i];
				previousScore[i] = -1e23;	// force score update

				localView.MoveToAbsolute(x+1, bottom);

				if (currentArray[i] == NULL)
				{
					//-------------------------------------------
					// Erase name
					//-------------------------------------------
					localView.SetColor(0x00);
					localView.DrawFilledRectangleToRelative(128,32);
					//-------------------------------------------
					// Erase score
					//-------------------------------------------
					Check(numericDisplay[i]);
					numericDisplay[i]->Erase(&localView);
					//-------------------------------------------
					// Erase channel
					//-------------------------------------------
					if (channelDisplay[i] != NULL)
					{
						Check(channelDisplay[i]);
						channelDisplay[i]->Erase(&localView);
					}
				}
				else
				{
					Check(currentArray[i]);
					//-------------------------------------------
					// Draw name
					//-------------------------------------------
					Check(application);
					Mission *current_mission = application->GetCurrentMission();
					Check(current_mission);
					//
					//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
					// Get the nameBitmap out of the mission
					//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
					//
					BitMap *name_bitmap =
						current_mission->
							GetLargeNameBitmap(currentArray[i]->playerBitmapIndex);

					if ( name_bitmap == NULL )
					{
						localView.SetColor(0x00);
						localView.DrawFilledRectangleToRelative(128,32);
					}
					else
					{
						int
							color = 255;

						if (options & useTeamColorOption)
						{
							Entity
								*entity = currentArray[i]->GetPlayerVehicle();

							if (entity != NULL)
							{
								color = determineEntityColor(entity);
							}
						}
						localView.SetColor(color);

						localView.DrawBitMapOpaque(
							0x00,	// background
							0,		// rotation
							name_bitmap
						);
					}
				}
			}
		}
		//----------------------------------------------------------------------
		// Draw scores if changed
		//----------------------------------------------------------------------
		for(i=0; i<maximumDisplayedPlayers; ++i)
		{
			RPPlayer
				*rp_player = (RPPlayer *) currentArray[i];

			if (rp_player == NULL)
			{
				numericDisplay[i]->Erase(&localView);
			}
			else
			{
				//-------------------------------------------
				// Get truncated score (no rounding)
				//-------------------------------------------
				int
					integer_score = (int) rp_player->currentScore;
				Scalar
					new_score = (Scalar) integer_score;

				if (previousScore[i] != new_score)
				{
					previousScore[i] = new_score;

					Check(numericDisplay[i]);
					//-------------------------------------------
					// Display scores for general players, or
					// only the runners in Martian Football
					//-------------------------------------------
					switch(rp_player->playerType)
					{
					case RPPlayer::GeneralPlayerType:
						if (i > currentRanking && !(options & showAllScoresOption))
						{
							numericDisplay[i]->Erase(&localView);
						}
						else
						{
							numericDisplay[i]->Draw(&localView, new_score);
						}
						break;

					case RPPlayer::RunnerPlayerType:
						numericDisplay[i]->Draw(&localView, new_score);
						break;

					default:
						numericDisplay[i]->Erase(&localView);
						break;
					}
				}
			}
		}
		//----------------------------------------------------------------------
		// Draw channels if changed
		//----------------------------------------------------------------------
		if (options & showIcomOption)
		{
			Player
				*this_player;
			L4Icom
				*this_intercom;
			int
				new_channel;

			if (! intercomEnabled)
			{
				if (previousIntercomEnabled == True)
				{
					//--------------------------------------------
					// We now have no intercom, clear channels
					//--------------------------------------------
					for(i=0; i<maximumDisplayedPlayers; ++i)
					{
						channelDisplay[i]->Erase(&localView);
						previousChannel[i] = Icom::undefinedChannel;
					}
				}
			}
			else
			{
				for(i=0; i<maximumDisplayedPlayers; ++i)
				{
					Check(channelDisplay[i]);

					if (currentArray[i] == NULL)
					{
						//--------------------------------------------
						// No intercom to reference
						//--------------------------------------------
						if (previousChannel[i] != Icom::undefinedChannel)
						{
							channelDisplay[i]->Erase(&localView);
							previousChannel[i] = Icom::undefinedChannel;
						}
					}
					else
					{
						// HACK - eventually HIDE the channel if not the same team

						//-------------------------------------------
						// Get the channel number for the pilot
						//-------------------------------------------
						Check(currentArray[i]);
						this_player = currentArray[i];
						Check(this_player);
						this_intercom = (L4Icom *) this_player->GetIntercom();
						Check(this_intercom);
						new_channel =
							this_intercom->GetChannel() -
							this_intercom->GetChannelOffset();
						Verify(new_channel >= 0);
						Verify(new_channel < 7);
						//-------------------------------------------
						// Update if changed
						//-------------------------------------------
						if (previousChannel[i] != new_channel)
						{
							previousChannel[i] = new_channel;

							if (new_channel == Icom::undefinedChannel)
							{
								//-------------------------------------------
								// Not on the 'com, clear channel number
								//-------------------------------------------
								channelDisplay[i]->Erase(&localView);
							}
							else
							{
								//-------------------------------------------
								// On the 'com, display channel number
								// (channels are internally 0..N, add one
								// to match labels on display)
								//-------------------------------------------
								channelDisplay[i]->Draw(&localView, new_channel+1);
							}
						}
					}
				}
			}
			//---------------------------------
			// Update 'previousIntercomEnabled'
			//---------------------------------
			previousIntercomEnabled = intercomEnabled;
		}
		//----------------------------------------------------------------------
		// Draw boxes if changed
		//----------------------------------------------------------------------
		if (previousRanking != currentRanking)
		{
			//---------------------------------
			// Erase old box
			//---------------------------------
			if (previousRanking >= 0)
			{
				if (previousRanking < maximumDisplayedPlayers)
				{
					localView.SetColor(0x00);
					localView.MoveToAbsolute(x,y-(previousRanking*spacing)-1);
					localView.DrawRectangleToRelative(width,34);
				}
			}
			//---------------------------------
			// Draw new box
			//---------------------------------
			if (currentRanking >= 0)
			{
				if (currentRanking < maximumDisplayedPlayers)
				{
					localView.SetColor(255);
					localView.MoveToAbsolute(x,y-(currentRanking*spacing)-1);
					localView.DrawRectangleToRelative(width,34);
				}
			}
			//---------------------------------
			// Update 'previousRanking'
			//---------------------------------
			previousRanking = currentRanking;
		}
	}

	Check_Fpu();
}

//############################################################################
// ConfigMap
//############################################################################

MethodDescription
	ConfigMap::methodDescription =
		{
			"configMap",
			ConfigMap::Make,
			{
				{ ParameterDescription::typeRate, NULL },		// rate ID
				{ ParameterDescription::typeModeMask, NULL },// mode mask
				{ ParameterDescription::typeString, NULL },	// "unmapped" icon
				{ ParameterDescription::typeString, NULL },	// "other" icon
				{ ParameterDescription::typeString, NULL },	// "my" icon
				{ ParameterDescription::typeString, NULL },	// "both" icon
				PARAMETER_DESCRIPTION_END
			}
		};

Logical
	ConfigMap::Make(
		int display_port_index,
		Vector2DOf<int> position,
		Entity */*entity*/,
		GaugeRenderer * gauge_renderer
	)
{
	ParameterDescription
		*parameterList = methodDescription.parameterList;


#	if DEBUG_LEVEL > 0
	parameterList[0].CheckIt(ParameterDescription::typeRate);		// group ID
	parameterList[1].CheckIt(ParameterDescription::typeModeMask);	// mode mask
	parameterList[2].CheckIt(ParameterDescription::typeString);		// "unmapped"
	parameterList[3].CheckIt(ParameterDescription::typeString);		// "other"
	parameterList[4].CheckIt(ParameterDescription::typeString);		// "my"
	parameterList[5].CheckIt(ParameterDescription::typeString);		// "both"
#	endif

	Test_Tell("ConfigMap::Make\n");

#	if DEBUG_LEVEL > 0
	ConfigMap
		*config_map =
#	endif
			new ConfigMap(
				parameterList[0].data.rate,
				parameterList[1].data.modeMask,
				(L4GaugeRenderer *) gauge_renderer,
				0, 										// unsigned int owner_ID
				display_port_index, 					// int graphics_port_number
				position.x, position.y,
				parameterList[2].data.string,	// "unmapped" icon
				parameterList[3].data.string,	// "mapped to other" icon
				parameterList[4].data.string,	// "mapped exclusively to us" icon
				parameterList[5].data.string	// "mapped to us and others" icon
			);

	Register_Object(config_map);
	//----------------------------------------------------------
	// Make sure the icons were properly placed in the warehouse
	//----------------------------------------------------------
	Check(gauge_renderer);
	L4Warehouse
		*warehouse = (L4Warehouse *) gauge_renderer->warehousePointer;
	Check(warehouse);

	for(int i=2; i<=5; ++i)
	{
		if (warehouse->pixelMap8Bin.Get(parameterList[i].data.string) == NULL)
		{
			Test_Tell(
				"ConfigMap::Missing icon'" << parameterList[i].data.string << "'\n"
			);
			Check_Fpu();
			return False;
		}
		else
		{
			warehouse->pixelMap8Bin.Release(parameterList[i].data.string);
		}
	}
	//--------------------------------------------------------
	// Everything's ok
	//--------------------------------------------------------
	Test_Tell("ConfigMap::Make OK\n");
	Check_Fpu();
	return True;
}

ConfigMap::ConfigMap(
	GaugeRate rate,
	ModeMask mode_mask,
	L4GaugeRenderer *renderer,
	unsigned int owner_ID,
	int graphics_port_number,
	int left, int bottom,
	const char *unmapped_icon,
	const char *other_icon,
	const char *my_icon,
	const char *both_icon,
	const char *identification_string
):
	GraphicGauge(
		rate,
		mode_mask,
		renderer,
		owner_ID,
		graphics_port_number,
		identification_string
	)
{
	Test_Tell("ConfigMap creator\n" << std::flush);
	Check_Pointer(this);
	// graphics_port may be NULL!

	//-------------------------------------------------
	// Make local copies of image names
	//-------------------------------------------------
	iconName[0] = nameCopy(unmapped_icon);
	iconName[1] = nameCopy(other_icon);
	iconName[2] = nameCopy(my_icon);
	iconName[3] = nameCopy(both_icon);
	//-------------------------------------------------
	// Preload images from warehouse
	//-------------------------------------------------
#	if defined(PRELOAD_ART)
	{
		int
			i;
		Check(renderer);
		L4Warehouse
			*warehouse = (L4Warehouse *) renderer->warehousePointer;
		Check(warehouse);

		for(i=0; i<4; ++i)
		{
			warehouse->pixelMap8Bin.Get(iconName[i]);
		}
	}
#	endif
	//-----------------------------------------------------------
	// Initialize values
	//-----------------------------------------------------------
#	if DEBUG_LEVEL > 0
	{
		int
			i;
		for(i=0; i<4; ++i)
		{
			Check_Pointer(iconName[i]);
		}
	}
#	endif

	localView.SetOrigin(left, bottom);

	linkedEntity = NULL;
	Check_Fpu();
}

ConfigMap::~ConfigMap()
{
	Test_Tell("ConfigMap destructor\n" << std::flush);
	Check(this);
	//-------------------------------------------------
	// Unlock preloaded art
	//-------------------------------------------------
#	if defined(PRELOAD_ART)
	{
		int
			i;
		Check(renderer);
		L4Warehouse
			*warehouse = (L4Warehouse *) renderer->warehousePointer;
		Check(warehouse);

		for(i=0; i<4; ++i)
		{
			warehouse->pixelMap8Bin.Release(iconName[i]);
		}
	}
#	endif
	//-------------------------------------------------
	// Delete local copies of image names
	//-------------------------------------------------
	for(int i=0; i<4; ++i)
	{
		Unregister_Pointer(iconName[i]);
		delete[] iconName[i];
		iconName[i] = NULL;
	}

	Check_Fpu();
}


Logical
	ConfigMap::TestInstance() const
{
	return GraphicGauge::TestInstance();
}

void
	ConfigMap::ShowInstance(char */*indent*/)
{
	Check_Fpu();
}

void
	ConfigMap::LinkToEntity(Entity *entity)
{
	Test_Tell("ConfigMap::LinkToEntity\n" << std::flush);
	Check(this);

	linkedEntity = entity;
	Check_Fpu();
}

void
	ConfigMap::BecameActive()
{
	Check(this);

	int
		i;

	for(i=0; i<4; ++i)
	{
		previousState[i] = -1;
	}
	Check_Fpu();
}

struct ConfigSet
{
	int yOffset;
	int buttonNumber;
};

void
	ConfigMap::Execute()
{
	static ConfigSet
		configSet[4] =
			{
				{   0, LBE4ControlsManager::ButtonJoystickPinky     },
				{  58, LBE4ControlsManager::ButtonJoystickThumbLow  },
				{ 118, LBE4ControlsManager::ButtonJoystickTrigger   },
				{ 178, LBE4ControlsManager::ButtonJoystickThumbHigh }
			};

	Test_Tell("ConfigMap::Execute\n" << std::flush);
	Check(this);

	if (linkedEntity == NULL)
	{
		Check_Fpu();
		return;
	}

	//-----------------------------------------------------------
	// Get current preset mode mask
	//-----------------------------------------------------------
	L4VTVControlsMapper
		*mapper = Cast_Object(
			L4VTVControlsMapper*,
			linkedEntity->GetSubsystem(VTV::ControlsMapperSubsystem)
		);
	Check(mapper);

	ModeMask
		preset_mode_mask = mapper->GetPresetModeMask();
	//-----------------------------------------------------------
	// Get pointer to controls
	//-----------------------------------------------------------
	Check(application);
	LBE4ControlsManager
		*controls = (LBE4ControlsManager *) application->GetControlsManager();
	Check(controls);
	//-----------------------------------------------------------
	// Get warehouse pointer
	//-----------------------------------------------------------
	Check(renderer);
	L4Warehouse
		*warehouse = (L4Warehouse *) renderer->warehousePointer;
	Check(warehouse);
	//-----------------------------------------------------------
	// Scan controls, searching for mapping changes
	//-----------------------------------------------------------
	int
		i,
		state;

	for(i=0; i<4; ++i)
	{
		if (
				(controls->activeDestination == NULL)
				&&
				(controls->activeMessageID == 0)
			)
		{
			state = 0;
		}
		else
		{
			state = controls ->
				buttonGroup[configSet[i].buttonNumber].
					GetMapState(
						controls->activeDestination,
						controls->activeReceiver,
						controls->activeMessageID,
						preset_mode_mask
				);
		}

		Verify(state >= 0);
		Verify(state <= 3);

		if (previousState[i] != state)
		{
			previousState[i] = state;
			//--------------------------------------------------
			// Get, lock icon from warehouse
			//--------------------------------------------------
			PixelMap8
				*pixelmap = warehouse->pixelMap8Bin.Get(iconName[state]);

			if (pixelmap != NULL)
			{
				//--------------------------------------------------
				// Draw it
				//--------------------------------------------------
				localView.MoveToAbsolute(0, configSet[i].yOffset);
				localView.DrawPixelMap8(
					False,	// opaque
					0,			// rotation
					pixelmap
				);
			}
			//--------------------------------------------------
			// Release icon from warehouse
			//--------------------------------------------------
			Check(warehouse);
			warehouse->pixelMap8Bin.Release(iconName[i]);
		}
	}
	Check_Fpu();
}


//*****************************************************************************
// MessageBoard
//*****************************************************************************
MethodDescription
	MessageBoard::methodDescription =
		{
			"messageBoard",
			MessageBoard::Make,
			{
				{ ParameterDescription::typeRate, NULL },		// rate ID
				{ ParameterDescription::typeModeMask, NULL },// mode mask
				{ ParameterDescription::typeVector, NULL },		// size
				{ ParameterDescription::typeString, NULL },		// font
				{ ParameterDescription::typeString, NULL },		// message map
				{ ParameterDescription::typeColor, NULL },		// background
				{ ParameterDescription::typeColor, NULL },		// background
				PARAMETER_DESCRIPTION_END
			}
		};

Logical
	MessageBoard::Make(
		int display_port_index,
		Vector2DOf<int> position,
		Entity */*entity*/,
		GaugeRenderer *gauge_renderer
	)
{
	Test_Tell("MessageBoard::Make\n");
	Check(gauge_renderer);

	ParameterDescription
		*parameterList = methodDescription.parameterList;

#	if DEBUG_LEVEL > 0
	parameterList[0].CheckIt(ParameterDescription::typeRate);		// group ID
	parameterList[1].CheckIt(ParameterDescription::typeModeMask);	// mode mask
	parameterList[2].CheckIt(ParameterDescription::typeVector);	// size
	parameterList[3].CheckIt(ParameterDescription::typeString);	// font
	parameterList[4].CheckIt(ParameterDescription::typeString);	// message map
	parameterList[5].CheckIt(ParameterDescription::typeColor);	// bg
	parameterList[6].CheckIt(ParameterDescription::typeColor);	// numeric
#	endif

#	if DEBUG_LEVEL > 0
	MessageBoard
		*annunciator =
#	endif
			new MessageBoard(
				parameterList[0].data.rate,
				parameterList[1].data.modeMask,
				(L4GaugeRenderer *) gauge_renderer,
				0, 										// unsigned int owner_ID
				display_port_index, 					// int graphics_port_number
				position.x, position.y,
				position.x + parameterList[2].data.vector.x, 
				position.y + parameterList[2].data.vector.y,
				parameterList[3].data.string,		// font
				parameterList[4].data.string,		// message map
				parameterList[5].data.color,		// bg color
				parameterList[6].data.color		// numeric color
			);

	Register_Object(annunciator);
	//----------------------------------------------------------
	// Make sure the art was properly placed in the warehouse
	//----------------------------------------------------------
	Check(gauge_renderer);
	L4Warehouse
		*warehouse = (L4Warehouse *) gauge_renderer->warehousePointer;
	Check(warehouse);

	for(int i=3; i<=4; ++i)
	{
		if (warehouse->bitMapBin.Get(parameterList[i].data.string) == NULL)
		{
			Test_Tell(
				"MessageBoard::Missing art '" <<
				parameterList[i].data.string << "'\n"
			);
			Check_Fpu();
			return False;
		}
		else
		{
			warehouse->bitMapBin.Release(parameterList[i].data.string);
		}
	}
	//--------------------------------------------------------
	// Everything's ok
	//--------------------------------------------------------
	Test_Tell("MessageBoard::Make\n");
	Check_Fpu();
	return True;
}

MessageBoard::MessageBoard(
	GaugeRate rate,
	ModeMask mode_mask,
	L4GaugeRenderer *renderer,
	unsigned int owner_ID,
	int graphics_port_number,

	int left, int bottom,
	int right, int top,
	const char *font_name,
	const char *message_name,
	int bg_color,
	int numeric_color,
	const char *identification_string
):
	GraphicGauge(
		rate,
		mode_mask,
		renderer,
		owner_ID,
		graphics_port_number,
		identification_string
	)
{
	Test_Tell("MessageBoard::MessageBoard\n");
	Check_Pointer(this);

	// graphics_port may be NULL!

	Check_Pointer(font_name);
	Check_Pointer(message_name);

	//-----------------------------------------------------------
	// Make local copy of image name
	//-----------------------------------------------------------
	messageName = nameCopy(message_name);
	Check_Pointer(messageName);
	//-----------------------------------------------------------
	// Create warehouse pointer
	//-----------------------------------------------------------
	Check(renderer);                  
	L4Warehouse
		*warehouse = (L4Warehouse *) renderer->warehousePointer;
	Check(warehouse);
	//-----------------------------------------------------------
	// Preload message bitmap
	//-----------------------------------------------------------
#	if defined(PRELOAD_ART)
		warehouse->bitMapBin.Get(messageName);
#	endif
	//-----------------------------------------------------------
	// Set position of graphicsView within the graphicsPort
	// (which also specifies the clipping and origin)
	//-----------------------------------------------------------
	Verify(right > left);
	Verify(top > bottom);
	localView.SetPositionWithinPort(left, bottom, right, top);
	//-----------------------------------------------------------
	// Initialize internal values
	//-----------------------------------------------------------
	backgroundColor = bg_color;
	linkedEntity = NULL;
	//--------------------------------
	// Get, lock font from warehouse
	//--------------------------------
	BitMap
		*font = warehouse->bitMapBin.Get(font_name);

	if (font == NULL)
	{
		fontHeight = 0;
	}
	else
	{
		fontHeight = font->Data.Size.y;
	}
	//-----------------------------------------------------------
	// Create numeric display
	//-----------------------------------------------------------
	Check(renderer);
	Check(renderer->warehousePointer);
	numericDisplay = new NumericDisplay(
		(L4Warehouse *) renderer->warehousePointer,
		14, 0,		// position relative to local origin
		font_name,
		8,
		NumericDisplay::signedBlankedZerosFormat,
		0,
		numeric_color
	);
	Register_Object(numericDisplay);

	//-----------------------------------------------------------
	// Release font from warehouse
	//-----------------------------------------------------------
	Check(warehouse);
	warehouse->bitMapBin.Release(font_name);

	Check_Fpu();
}

MessageBoard::~MessageBoard()
{
	Test_Tell("MessageBoard::~MessageBoard\n");
	Check(this);
	//-------------------------------------------------
	// Unlock preloaded art
	//-------------------------------------------------
#	if defined(PRELOAD_ART)
		Check(renderer);
		L4Warehouse
			*warehouse = (L4Warehouse *) renderer->warehousePointer;
		Check(warehouse);
		warehouse->bitMapBin.Release(messageName);
#	endif
	//-------------------------------------------------
	// Delete local copies of image name
	//-------------------------------------------------
	Unregister_Pointer(messageName);
	delete[] messageName;
	messageName = NULL;
	//-----------------------------------------------------------
	// Delete numeric display
	//-----------------------------------------------------------
	Unregister_Object(numericDisplay);
	delete numericDisplay;
	numericDisplay = NULL;

	// base class deletes all connections in Gauge::~Gauge
	Check_Fpu();
}

Logical
	MessageBoard::TestInstance() const
{
	return GraphicGauge::TestInstance();
}

void
	MessageBoard::ShowInstance(char *indent)
{
	Check(this);

	std::cout << indent << "MessageBoard:\n";

	char
		temp[80];

	Str_Copy(temp,indent, 80);
	Str_Cat(temp,"...", 80);

	std::cout << temp << "backgroundColor=" << backgroundColor << "\n";
	std::cout << temp << "linkedEntity   =" << linkedEntity << "\n";
	std::cout << temp << "numericDisplay =" << numericDisplay << "\n";
	GraphicGauge::ShowInstance(temp);

	Str_Cat(temp,"...", 80);
	Check(numericDisplay);
	numericDisplay->ShowInstance(temp);

	Check_Fpu();
}

void
	MessageBoard::BecameActive()
{
	Test_Tell("MessageBoard::BecameActive\n" << std::flush);
	Check(this);

	previousPlayerPointer = (Player *) deliberatelyBogusPointerValue;
	previousMessageType = -2;
	previousValue = (Scalar) 2e-22;

	Check_Fpu();
}

void
	MessageBoard::LinkToEntity(Entity *entity)
{
	Test_Tell("MessageBoard::LinkToEntity\n" << std::flush);
	Check(this);

	linkedEntity = entity;
	Check_Fpu();
}

void
	MessageBoard::Execute()
{
	Test_Tell("MessageBoard::Execute\n" << std::flush);
	Check(this);

	if (linkedEntity == NULL)
	{
		return;
	}
	Check(linkedEntity);

	//--------------------------------------------------
	// Get a pointer to the local player object
	// (or else return)
	//--------------------------------------------------
	RPPlayer
		*owner_player = (RPPlayer *) linkedEntity->GetPlayerLink();

	if (owner_player == NULL)
	{
		return;
	}
	Check(owner_player);
	//--------------------------------------------------
	// Get message data
	//--------------------------------------------------
	Player
		*message_player;
	int
		message_type;
	Scalar
		message_value;

	{
		RPPlayer__StatusMessage
			*message_pointer = (RPPlayer__StatusMessage *)
				owner_player->statusMessagePointer;

		if (message_pointer == NULL)
		{
			message_player = NULL;
			message_type = RPPlayer::RPStatusMessage::NoMessage;
			message_value = 0.0;
		}
		else
		{
			Check_Pointer(message_pointer);

			message_player = message_pointer->playerInvolved;
			message_type = message_pointer->messageType; 
			message_value = message_pointer->damageAmount;
		}
	}

	//--------------------------------------------------
	// Draw player name
	//
	// Warning - previousPlayerPointer is deliberately
	// set to an illegal value in BecameActive() in order
	// to force the next 'if' statement to execute.
	// This is safe because we never directly use
	// previousPlayerPointer without loading first
	// loading it from message_player.
	//--------------------------------------------------
	if (previousPlayerPointer != message_player)
	{
		localView.MoveToAbsolute(0,fontHeight+1);

		if (message_player == NULL)
		{
			//------------------------------
			// No player, erase area
			//------------------------------
			localView.SetColor(backgroundColor);

			// Note: previousPlayerPointer cannot be null 'cuz message_player was

			if (previousPlayerPointer == (Player *) deliberatelyBogusPointerValue)
			{
				//----------------------------------
				// No previous name, clear rectangle
				//----------------------------------
				localView.DrawFilledRectangleToRelative(128,32);
			}
			else
			{
				//----------------------------------------
				// Draw previous name in BG color to erase
				//----------------------------------------
				Check(previousPlayerPointer);

				localView.DrawBitMap(
					0,	// rotation
					(application->GetCurrentMission())->GetLargeNameBitmap(
						previousPlayerPointer->playerBitmapIndex
					)
				);
			}
		}
		else
		{
			Check(message_player);

			//------------------------------
			// Get player vehicle color
			//------------------------------
			Check(message_player->GetPlayerVehicle());
			localView.SetColor(
				determineEntityColor(message_player->GetPlayerVehicle())
			);
			//------------------------------
			// Get Bitmap from Mission
			//------------------------------
			Check(application);
			Check(application->GetCurrentMission());

			localView.DrawBitMapOpaque(
				backgroundColor,
				0,	// rotation
				(application->GetCurrentMission())->GetLargeNameBitmap(
					message_player->playerBitmapIndex
				)
			);
		}
		previousPlayerPointer = message_player;
	}
	//--------------------------------------------------
	// Draw message
	//--------------------------------------------------
	if (previousMessageType != message_type)
	{
		previousMessageType = message_type;

		localView.MoveToAbsolute(0,fontHeight+(1+32));

		if (previousMessageType < 0)
		{
			//--------------------------------------------------
			// Whoops, 'no message'. Erase it.
			//--------------------------------------------------
			localView.SetColor(backgroundColor);
			localView.DrawFilledRectangleToRelative(128,32);
		}
		else
		{
			//--------------------------------------------------
			// Choose sub-map from larger image
			//
			// The messageMap is a bitmap consisting of a
			// group of 32 128x32 messages arranged as
			// (4 across) by (8 high):
			//
			// +--------+--------+--------+--------+
			// |   28   |   29   |   30   |   31   |
			// +--------+--------+--------+--------+
			//        ....
			// +--------+--------+--------+--------+
			// |   12   |   13   |   14   |   15   |
			// +--------+--------+--------+--------+
			// |    8   |    9   |   10   |   11   |
			// +--------+--------+--------+--------+
			// |    4   |    5   |    6   |    7   |
			// +--------+--------+--------+--------+
			// |    0   |    1   |    2   |    3   |
			// +--------+--------+--------+--------+
			// (origin (0,0) is lower left!)
			//--------------------------------------------------
			Verify(previousMessageType < 32);

			int
				sx = (previousMessageType  & 3) << 7,	// times 128
				sy = (previousMessageType >> 2) << 5;	// times 32

			//-----------------------------------------------------------
			// Get player vehicle color
			//-----------------------------------------------------------
			Check(owner_player->GetPlayerVehicle());
			localView.SetColor(
				determineEntityColor(owner_player->GetPlayerVehicle())
			);
			//-----------------------------------------------------------
			// Get, lock image from warehouse
			//-----------------------------------------------------------
			Check(renderer);
			L4Warehouse
				*warehouse = (L4Warehouse *) renderer->warehousePointer;
			Check(warehouse);

			BitMap
				*message_map = warehouse->bitMapBin.Get(messageName);
			//-----------------------------------------------------------
			// Draw it
			//-----------------------------------------------------------
			if (message_map != NULL)
			{
				Check(message_map);

				localView.DrawBitMapOpaque(
					backgroundColor,
					0,						// rotation
					message_map,
					sx, sy, sx+127, sy+31
				);
			}
			//-----------------------------------------------------------
			// Release image from warehouse
			//-----------------------------------------------------------
			Check(warehouse);
			warehouse->bitMapBin.Release(messageName);
		}
	}
	//--------------------------------------------------
	// Draw numeric value
	//--------------------------------------------------
	if (previousValue != message_value)
	{
		previousValue = message_value;
		Check(numericDisplay);

		if ((previousValue > (Scalar) 1) || (previousValue < (Scalar) -1))
		{
			numericDisplay->Draw(&localView, previousValue);
		}
		else
		{
			numericDisplay->Erase(&localView);
		}
	}

	Check_Fpu();
}
