A Live Cam's map came up as furniture with nothing in it, and two confident explanations for that turned out to be wrong in a row - the cameraInit gauge page exists, and the gauge renderer IS linked to the camera ship, so both the page and its centre were fine all along. Two falsified guesses is the signal to measure instead, so RP412CAMLOG now traces the nav display: the scale and centre it resolves, the bounds it asks about, how many static and moving entities come back, and whether the sweep reaches the phase that actually draws. The ranking widget reports the players it can see with their rank and score, every five seconds so movement shows without flooding the log. It answered both questions on the first run, from a plain POD race: CamLog: nav scale 1000m across, 0.448 px/m, centre 12,416 CamLog: nav in bounds - 0 static, 0 moving CamLog: nav drew (phase 3 reached) A pod's nav map is as empty as a camera's. Sane scale, resolved centre, drawing phase reached, and nothing registered to draw - so this is not a Live Cam defect at all, it is a hole in whatever should be filling the gauge renderer's staticEntities and movingEntities, and it has been there for every station all along. A camera only made it obvious by having nothing else on the glass. The score, by contrast, tracks properly on a pod - 1000, 1005, 1148 across half a minute - so a camera host frozen at 1000 is genuinely camera shaped, and the ranking trace will say whether it sees the racer at all. Also here: a camera station's map defaults to the bottom-left corner rather than the pod's dead centre. Centre is where a cabinet wanted it and the worst place to put a panel on a picture. L4RADARPOS still overrides. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4104 lines
103 KiB
C++
4104 lines
103 KiB
C++
#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, ¤tScale, scale_value_pointer);
|
|
Register_Object(connection);
|
|
AddConnection(connection);
|
|
|
|
connection = new
|
|
GaugeConnectionDirectOf<Point3D *>(
|
|
0, ¤tPositionPointer, position_pointer);
|
|
Register_Object(connection);
|
|
AddConnection(connection);
|
|
|
|
connection = new
|
|
GaugeConnectionDirectOf<Quaternion *>(
|
|
0, ¤tAngularPointer, 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();
|
|
|
|
//
|
|
// RP412CAMLOG: the sweep reached the drawing phase at all. If the
|
|
// counts above are non-zero and this never appears, the gauge
|
|
// slice is not getting this display as far as phase 3.
|
|
//
|
|
if (RPCameraLog())
|
|
{
|
|
static int drew = 0;
|
|
if (drew < 3)
|
|
{
|
|
++drew;
|
|
DEBUG_STREAM << "CamLog: nav drew (phase 3 reached)\n" << std::flush;
|
|
}
|
|
}
|
|
|
|
// 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");
|
|
|
|
//
|
|
// RP412CAMLOG. Once, not per sweep - this runs at the gauge rate and
|
|
// would drown the log. Says whether the nav display has a sane centre
|
|
// and scale at all, which is the first question when the map comes up
|
|
// empty on a camera station.
|
|
//
|
|
if (RPCameraLog())
|
|
{
|
|
static int said = 0;
|
|
if (!said)
|
|
{
|
|
said = 1;
|
|
DEBUG_STREAM << "CamLog: nav scale " << currentScale
|
|
<< "m across, " << pixelsPerMeter << " px/m, centre "
|
|
<< viewing_position.x << "," << viewing_position.z
|
|
<< " bounds x " << xMin << ".." << xMax
|
|
<< " z " << zMin << ".." << zMax << "\n" << std::flush;
|
|
}
|
|
}
|
|
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
|
|
);
|
|
|
|
//
|
|
// RP412CAMLOG. The counts are the whole question: furniture with no
|
|
// content means either nothing fell inside the bounds above, or the
|
|
// drawing steps are not being reached. Reported for the first few
|
|
// sweeps only - the lists are rebuilt at the gauge rate.
|
|
//
|
|
if (RPCameraLog())
|
|
{
|
|
static int sweeps = 0;
|
|
if (sweeps < 5)
|
|
{
|
|
++sweeps;
|
|
ChainIteratorOf<Entity*> statics(staticEntityList.GetInstanceList());
|
|
ChainIteratorOf<Entity*> movers(movingEntityList.GetInstanceList());
|
|
DEBUG_STREAM << "CamLog: nav in bounds - " << statics.GetSize()
|
|
<< " static, " << movers.GetSize() << " moving\n" << std::flush;
|
|
}
|
|
}
|
|
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);
|
|
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// RP412MAPRATE - how many of the sixteen rate steps the map redraws
|
|
// on.
|
|
//
|
|
// The gauge renderer walks a 16-bit rate wheel: one bit per full
|
|
// pass over every active gauge, shifted right each pass and reset at
|
|
// the bottom (GAUGREND.cpp). A gauge redraws only on the step its
|
|
// configured rate names, so a map on one bit redraws once per
|
|
// SIXTEEN passes - and its update period is sixteen passes however
|
|
// cheap the redraw is.
|
|
//
|
|
// That was fine when a pass was quick. Racing, the background loop
|
|
// only gets what is left of the frame after the 3D, passes fall to
|
|
// about five a second, and sixteen of them is over three seconds
|
|
// between map updates - measured, on a 60 fps display with the 3D
|
|
// perfectly smooth. Nothing is slow here; the map is just waiting
|
|
// its turn on a wheel built for a machine that came round faster.
|
|
//
|
|
// Drawing on more of the steps costs one gauge's redraw per step,
|
|
// against a pass that runs ninety of them. It does not make the
|
|
// wheel turn faster - it stops the map needing a whole turn.
|
|
//-----------------------------------------------------------------
|
|
//
|
|
{
|
|
static int updates = -1;
|
|
if (updates < 0)
|
|
{
|
|
const char *setting = getenv("RP412MAPRATE");
|
|
updates = (setting != NULL) ? atoi(setting) : 16;
|
|
// 1..16, and only the powers of two divide the wheel evenly
|
|
if (updates > 16) updates = 16;
|
|
if (updates < 1) updates = 1;
|
|
}
|
|
if (updates > 1)
|
|
{
|
|
GaugeRate mask = 0;
|
|
for (int step = 0; step < 16; step += (16 / updates))
|
|
{
|
|
mask |= (GaugeRate)(0x8000 >> step);
|
|
}
|
|
//
|
|
// QUALIFIED, both of them. This constructor's first
|
|
// parameter is also called 'rate', so a bare assignment
|
|
// here writes the PARAMETER and leaves the member holding
|
|
// whatever the gauge data asked for - which is what it did,
|
|
// silently, and cost a long hunt for a writer that did not
|
|
// exist. oldRate is not shadowed, which is why it took the
|
|
// value and the pair disagreed.
|
|
//
|
|
// Gauge::Disable(False) restores rate from oldRate when the
|
|
// mode system enables a gauge, so both have to carry it or
|
|
// the first activation puts the old rate back.
|
|
//
|
|
Gauge::rate = mask;
|
|
Gauge::oldRate = mask;
|
|
}
|
|
|
|
}
|
|
|
|
needsStaticUpdate = True;
|
|
//
|
|
// Nothing drawn yet, so there is no picture to add a placement to and
|
|
// no scale it would be added at. The first pass builds both.
|
|
//
|
|
backgroundBuilt = False;
|
|
builtMinX = builtMinY = builtMinZ = (Scalar) 0;
|
|
builtMaxX = builtMaxY = builtMaxZ = (Scalar) 0;
|
|
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()))
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// A rebuild is already owed, or there is no picture to add to yet.
|
|
//
|
|
if (needsStaticUpdate || !backgroundBuilt)
|
|
{
|
|
needsStaticUpdate = True;
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Terrain arrives as you DRIVE - the interest system hands each piece
|
|
// over as it comes into range, not all of it at load - and this used
|
|
// to order a rebuild of the entire map for every one of them. A
|
|
// rebuild redraws every placement on the track, so the cost of one
|
|
// arriving piece was the whole track, and the map went seconds
|
|
// between updates on the tracks that draw the most: Tour De Mars at
|
|
// 351 placements and Ares' Armpits at 320, against 80-140 for a
|
|
// typical one. It is also not interruptible - the gauge loop checks
|
|
// its time slice BETWEEN gauges - so a rebuild stalled every other
|
|
// display with it.
|
|
//
|
|
// But the bounds are what set the scale, and the scale is what the
|
|
// whole cached picture was drawn at. An arrival that leaves the
|
|
// bounds alone leaves every placement already on the map exactly
|
|
// where it belongs, and the only thing missing from the picture is
|
|
// the new one. Draw that, and nothing else.
|
|
//
|
|
// Only a piece that moves an EDGE of the track changes the scale, and
|
|
// then the picture really is wrong everywhere and has to be redrawn.
|
|
// That is rare, and it gets rarer as the track fills in.
|
|
//
|
|
// RPL4GaugeRenderer::NotifyOfNewInterestingEntity adds the entity to
|
|
// staticEntities before chaining here, so these bounds already
|
|
// account for the arrival being judged.
|
|
//
|
|
Scalar
|
|
minX, minY, minZ,
|
|
maxX, maxY, maxZ;
|
|
|
|
Check(renderer);
|
|
renderer->GetStaticBounds(
|
|
&minX, &minY, &minZ,
|
|
&maxX, &maxY, &maxZ
|
|
);
|
|
|
|
if (minX != builtMinX || minY != builtMinY || minZ != builtMinZ ||
|
|
maxX != builtMaxX || maxY != builtMaxY || maxZ != builtMaxZ)
|
|
{
|
|
needsStaticUpdate = True;
|
|
}
|
|
else if (DrawStaticEntity(entity))
|
|
{
|
|
//
|
|
// Only when something was actually drawn. Terrain without a
|
|
// GaugeImage never reaches the map - on these tracks that is
|
|
// most of it, 256 of Tour De Mars' 607 - and blitting the
|
|
// background again for one of those is pure cost.
|
|
//
|
|
needsScreenUpdate = True;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// NotifyOfBecomingUninterestingEntity
|
|
//#############################################################################
|
|
//
|
|
// The map showed only currently-interesting terrain before any of this,
|
|
// and it still does. A placement cannot be un-drawn from a composited
|
|
// picture, so a departure is the one case left that costs a full rebuild.
|
|
//
|
|
// Nothing used to listen for this at all: departures were swept up by the
|
|
// rebuild that the very next ARRIVAL ordered, which is no longer ordered.
|
|
// Without this the map would keep showing terrain that had gone until
|
|
// something moved the bounds.
|
|
//
|
|
void
|
|
GPS::NotifyOfBecomingUninterestingEntity(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());
|
|
|
|
Entity
|
|
*entity;
|
|
|
|
Verify(!Small_Enough(pixelsPerMeter));
|
|
|
|
int
|
|
drawn = 0;
|
|
|
|
while ((entity=i.ReadAndNext()) != NULL)
|
|
{
|
|
Check(entity);
|
|
DrawStaticEntity(entity);
|
|
}
|
|
|
|
//
|
|
// The picture now matches these bounds, and they are what the scale
|
|
// everything on it was drawn at came from. Remember them: an arrival
|
|
// that leaves them alone can be added to this picture rather than
|
|
// replacing it.
|
|
//
|
|
builtMinX = minX;
|
|
builtMinY = minY;
|
|
builtMinZ = minZ;
|
|
builtMaxX = maxX;
|
|
builtMaxY = maxY;
|
|
builtMaxZ = maxZ;
|
|
backgroundBuilt = True;
|
|
|
|
//-----------------------------------------------------------
|
|
// Redraw new background, restart moving entity display
|
|
//-----------------------------------------------------------
|
|
BecameActive();
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// DrawStaticEntity
|
|
//#############################################################################
|
|
//
|
|
// One placement onto the cached background, at the scale that background
|
|
// was built at. Shared by the full rebuild above and by the single-arrival
|
|
// path in NotifyOfNewInterestingEntity, so the two cannot drift into
|
|
// drawing the same track two different ways.
|
|
//
|
|
Logical
|
|
GPS::DrawStaticEntity(Entity *entity)
|
|
{
|
|
Check(this);
|
|
Check(entity);
|
|
Check(background);
|
|
Check(renderer);
|
|
|
|
L4Warehouse
|
|
*warehouse = (L4Warehouse *) renderer->warehousePointer;
|
|
Check(warehouse);
|
|
|
|
L4GaugeImage
|
|
*gauge_image =
|
|
warehouse->gaugeImageBin.GetIfAlreadyExists(entity->GetResourceID());
|
|
if (gauge_image == NULL)
|
|
{
|
|
return False; // nothing of it appears on the map
|
|
}
|
|
Check(gauge_image);
|
|
|
|
L4BytePort
|
|
backgroundPort(background, "background", 0);
|
|
GraphicsView
|
|
backgroundView(&backgroundPort);
|
|
backgroundView.SetOrigin(width>>1, height>>1);
|
|
|
|
Vector3D
|
|
scaling_vector;
|
|
scaling_vector.x = pixelsPerMeter;
|
|
scaling_vector.y = pixelsPerMeter;
|
|
scaling_vector.z = pixelsPerMeter;
|
|
|
|
AffineMatrix
|
|
worldToView,
|
|
localToView;
|
|
|
|
worldToView.BuildIdentity();
|
|
worldToView *= centeringOffset; // translation
|
|
worldToView *= scaling_vector;
|
|
|
|
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());
|
|
return True;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// RP412CAMLOG. Says what the ranking widget can see: how many
|
|
// players are in the group and what rank and score each carries.
|
|
// A score that never moves is either not replicated to a camera
|
|
// host or simply has not changed - this tells the two apart. Every
|
|
// few seconds, not every sweep.
|
|
//
|
|
if (RPCameraLog())
|
|
{
|
|
static Scalar next_say = 0.0f;
|
|
if ((Scalar) Now() >= next_say)
|
|
{
|
|
next_say = ((Scalar) Now()) + 5.0f;
|
|
|
|
ChainIteratorOf<Node*> say(all_players->groupMembers);
|
|
Player *seen;
|
|
int shown = 0;
|
|
DEBUG_STREAM << "CamLog: ranking sees " << say.GetSize()
|
|
<< " player(s):";
|
|
while ((seen = (Player*) say.ReadAndNext()) != NULL && shown < 8)
|
|
{
|
|
++shown;
|
|
DEBUG_STREAM << " [bmp " << seen->playerBitmapIndex
|
|
<< " rank " << seen->playerRanking
|
|
<< " score " << (int) seen->currentScore << "]";
|
|
}
|
|
DEBUG_STREAM << " ourRank=" << currentRanking << "\n" << std::flush;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------
|
|
// 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();
|
|
}
|