/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1998  Riley Rainey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software Foundaation,
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

#include "pm.h"
#include "eng.xbm"
#include "flaps0.xbm"
#include "flaps1.xbm"
#include "flaps2.xbm"
#include "flaps3.xbm"
#include "handleUp.xbm"
#include "handleDn.xbm"
#include "gearUp.xbm"
#include "gearTran.xbm"
#include "gearDown.xbm"
#include <stdio.h>
#include <X11/Xutil.h>
#ifndef __hpux
#include <X11/Xos.h>
#endif
#include <X11/cursorfont.h>
#ifdef HAVE_DIS
#include "dis.h"
#endif

extern void resizePlayerWindow(craft * c, 
							   viewer * u, 
							   int width, 
							   int height, 
							   int initial_flag);
extern void blackBoxKillPlayer(int id);
extern void printValidAircraft(int s);
extern void buildEulerMatrix(double roll, double pitch, double heading, 
							 VMatrix * m);
extern void transpose(VMatrix * m, VMatrix * r);

#define BORDER	1

#define	ARG_FONT		"font"
#define ARG_RADAR_FONT		"radarfont"
#define	ARG_BORDER_COLOR	"bordercolor"
#define ARG_BORDER		"borderwidth"
#define	ARG_GEOMETRY		"geometry"
/* #define DEFAULT_BACKGROUND   "#93bde4"   /* my version of sky blue */
/* #define DEFAULT_BACKGROUND   "#7491ae"   /* my version of sky blue */
#define DEFAULT_BACKGROUND	"#7c99b6"	/* my version of sky blue */
#define DEFAULT_BORDER		"black"
#define DEFAULT_RADAR_FONT	"-misc-fixed-medium-*-*-*-*-120-*-*-*-*-*-*"

#define SW_BORDER	      1
#define SW_BACKGROUND	  2
#define SW_HUDFONT	      3
#define SW_GEOM		      4
#define SW_RADARFONT	  5
#define SW_TEAM		      6
#define SW_DEFAULT_VISUAL 8
#define SW_PLANE	      9
#define SW_LIST_PLAYER	 10
#define SW_MONOCHROME	 11
/* 12 unused */
#define SW_PASSIVE       13
#define SW_LATITUDE      14
#define SW_LONGITUDE     15
#define SW_ALTITUDE      16
#define SW_AIRSPEED_KTS  17
#define SW_HEADING       18
#define SW_END_GAME      19
#define SW_LAST 19             /* last entry in list */
               
struct {
	char     *sw;
	int       value;
} swt[] = {
	"-bw",        SW_BORDER,
	"-skycolor",  SW_BACKGROUND,
	"-hudfont",   SW_HUDFONT,
	"-radarfont", SW_RADARFONT,
	"-geometry",  SW_GEOM,
	"-team",      SW_TEAM,
	"-stealth",   SW_PASSIVE,
	"-cmap",      SW_DEFAULT_VISUAL,
	"-mono",      SW_MONOCHROME,
	"-plane",     SW_PLANE,
	"-list",      SW_LIST_PLAYER,
	"-latitude",  SW_LATITUDE,
	"-longitude", SW_LONGITUDE,
	"-altitude",  SW_ALTITUDE,
	"-airspeed-kts", SW_AIRSPEED_KTS,
	"-heading",   SW_HEADING,
	"-end-game",  SW_END_GAME,
	NULL, 0
}, *swp;

void
recoverAcmArgv(char *args, int *argc, char **argv)
{

	char     *s;

	argv[0] = ACM;
	argv[1] = args;

	if (*args == '\0') {
		*argc = 1;
		argv[1] = (char *) NULL;
		return;
	}
	else
		*argc = 2;

	for (s = args; *s;) {
		if (*s == '|') {
			*s = '\0';
			argv[(*argc)++] = ++s;
		}
		else
			++s;
	}

	argv[*argc] = (char *) NULL;
}

Visual   *
get_pseudo_visual(Display * display, int screen, unsigned int *depth)
{

	XVisualInfo vTemplate;
	XVisualInfo *visualList;
	int       i, visualsMatched;

/*
 *  Get all Visuals on this screen
 */

	vTemplate.screen = screen;
	vTemplate.depth = 8;
	visualList = XGetVisualInfo(display, VisualScreenMask | VisualDepthMask,
								&vTemplate, &visualsMatched);

	if (visualsMatched == 0)
		return NULL;

/*
 *  look first for a suitable PseudoColor visual, then a GreyScale visual
 *  if no PseudoColor is present
 */

	for (i = 0; i < visualsMatched; ++i) {
		if (visualList[i].visual->class == TrueColor &&
			visualList[i].depth == 8) {
			*depth = visualList[i].depth;
			XFree((char *) visualList);
			return visualList[i].visual;
		}
	}

	for (i = 0; i < visualsMatched; ++i) {
		if (visualList[i].visual->class == GrayScale &&
			visualList[i].depth >= 4) {
			*depth = visualList[i].depth;
			XFree((char *) visualList);
			return visualList[i].visual;
		}
	}

	XFree((char *) visualList);

	return NULL;
}

int
newPlayer(int s, char *display, char *logname, char *switches)
{

	char     *fontName;			/* Name of font for string */
	XSizeHints xsh;				/* Size hints for window manager */
	Colormap  cmap;
	XGCValues gcv;
	unsigned long pad;			/* Font size parameters */
	unsigned long bd;			/* Pixel values */
	unsigned long bw;			/* Border width */
	char     *tempstr;			/* Temporary string */
	XColor    color;			/* Temporary color */
	char     *geomSpec;			/* Window geometry string */
	XWMHints  xwmh;				/* Window manager hints */
	Cursor    cursor;
	char    **c;
	char      err[256];
	static char *background = NULL;
	int       borderWidth = -1;
	int       player;
	viewer   *u;
	craft    *cf;
	int       argc;
	char     *argv[32];
	int       screen;
	char     *hudfont = NULL, *radarfont = NULL;
	int       team = 1;
	char     *plane = NULL;		/* name of plane type */
	int       obsrver = -1;
	int       width, height;	/* dimensions of main window */
	double    scale;
	unsigned  depth, mono = 0;
	long      win_attr_mask;
	Visual   *theVisual;
	XSetWindowAttributes window_attributes;
	int       useDefaultVisual = 0;
	int       passive = 0;
	Atom      atom[2];
	int       overrides[SW_LAST+1];
	double    overrideLatitude, overrideLongitude, overrideAltitude;
	double    overrideHeading_rad, overrideAirspeed_fps;
	int       end_game = 0;

#ifdef HAVE_DIS
	double    disLocation[3];
	double    disZeroVec[3];
	double    disOrientation[3];
	int       disType;

#endif

	memset(overrides, 0, sizeof(overrides));

	recoverAcmArgv(switches, &argc, argv);

	geomSpec = NULL;
	u = (viewer *) malloc(sizeof(viewer));
	if (!u) {
		printf ("unable to allocate viewer\n");
		exit (1);
	}
	u->watchedCraft = NULL;
	u->viewer_state = ViewerStateNormal;
	u->browseBase = 0;
	u->browseSelectedItem = -1;
	u->browseClickTime = 0;
	u->viewDirection.x = 1.0;
	u->viewDirection.y = 0.0;
	u->viewDirection.z = 0.0;
	u->viewUp.x = 0.0;
	u->viewUp.y = 0.0;
	u->viewUp.z = -1.0;

/*
 *  Parse command line
 */

	for (c = &argv[1]; *c != (char *) NULL; ++c)
		if (**c == '-') {
			for (swp = &swt[0]; swp->value != 0; ++swp)
				if (strcmp(swp->sw, *c) == 0) {

					switch (swp->value) {

					case SW_GEOM:
						geomSpec = *(++c);
						break;

					case SW_END_GAME:
						end_game = 1;
						break;

					case SW_BORDER:
						borderWidth = atoi(*(++c));
						break;

					case SW_BACKGROUND:
						background = *(++c);
						break;

					case SW_HUDFONT:
						hudfont = *(++c);
						break;

					case SW_RADARFONT:
						radarfont = *(++c);
						break;

					case SW_TEAM:
						team = atoi(*(++c));
						break;

					case SW_PASSIVE:
					    passive = 1;
						break;

					case SW_PLANE:
						plane = *(++c);
						break;

					case SW_MONOCHROME:
						mono = 1;
						break;

					case SW_LATITUDE:
						overrideLatitude = DEGtoRAD(atof(*(++c)));
						overrides[SW_LATITUDE] = 1;
						break;

					case SW_LONGITUDE:
						overrideLongitude = DEGtoRAD(atof(*(++c)));
						overrides[SW_LONGITUDE] = 1;
						break;

					case SW_ALTITUDE:
						overrideAltitude = FEETtoMETERS(atof(*(++c)));
						overrides[SW_ALTITUDE] = 1;
						break;

					case SW_AIRSPEED_KTS:
						overrideAirspeed_fps = KTStoFPS(atof(*(++c)));
						if (overrideAirspeed_fps > KTStoFPS(2500.0)) {
							printf ("You really should slow down.\n");
							printf ("At least to less than 2500 knots.\n");
							exit (1);
						}
						overrides[SW_AIRSPEED_KTS] = 1;
						break;

					case SW_HEADING:
						overrideHeading_rad = DEGtoRAD(atof(*(++c)));
						overrides[SW_HEADING] = 1;
						break;

					case SW_LIST_PLAYER:
						sprintf(err, "\nname\t\tnumber\n");
						write(s, err, strlen(err));
						sprintf(err, "-------------------------\n");
						write(s, err, strlen(err));
						for (team = 0; team < MAXPLAYERS; team++) {
							if (ptbl[team].type == CT_PLANE) {
								sprintf(err, "%-16s  %d\n",
										ptbl[team].name, team);
								write(s, err, strlen(err));
							}
						}
						return -1;

					case SW_DEFAULT_VISUAL:
						useDefaultVisual = 0;
						break;
					}
					break;
				}
			if (swp->value == 0) {
				free((char *) u);
				sprintf(err, "%s: invalid switch %s", ACM, *c);
				write(s, err, strlen(err));
				return -1;
			}
		}

	if ( 1 ) {

		if (!plane) {
			plane = (team == 1) ? "F-16" : "MiG-29";
		}

		if ((player = newPlane(plane, -1)) < 0) {
			if (player == -1) {
				sprintf(err,
				"Sorry, no room for any more players at this moment.\n");
				write(s, err, strlen(err));
			}
			else {
				sprintf(err,
						"You have selected an unknown plane type.\n");
				write(s, err, strlen(err));
				printValidAircraft(s);
			}
			return -1;
		}

		cf = &ptbl[player];

/*
 *  assign an initial location basewd on the player's team.
 */

		if (team != 2) {
			team = 1;
		}

		cf->w.latitude  = teamLatLon[team].latitude;
		cf->w.longitude = teamLatLon[team].longitude;
		cf->w.z = cf->prevw.z = cf->w.z + teamLatLon[team].z;
		DISWorldCoordinatesToGeocentric(&cf->w,
									 (dis_world_coordinates *) & cf->Sg);
		GenerateWorldToLocalMatrix(&cf->w, &cf->XYZtoNED);

		cf->curHeading = teamHeading[team];

		buildEulerMatrix(cf->curRoll, cf->curPitch, cf->curHeading,
						 &(cf->trihedral));
	}

	/*
	 *  If we're passive (stealth mode), wipe out aircraft information;
	 *  this entry will become a placeholder for our browsing view.
	 */

	if ( passive ) {

		u->viewer_state = ViewerStateBrowsing;
		cf->type = CT_DIS_STEALTH;
		cf->cinfo = NULL;
		cf->radarMode = RM_DIS_BROWSE;

		if ( end_game ) {
			end_game_mode = 1;
		}

		/*
		 *  Stealth a specific entity?  Go ahead and set it as the subject.
		 *
		 *  This requires snooping for entities in the PDU stream prior
		 *  to looking for the entity in the table.
		 */

		if ( subjectEntitySpecified ) {
			craft *c;

			printf ("Building entity database ... ");
			fflush ( stdout );

			updateSimTimeFromSystemClock ();
			dis_snoop ( 5500 );

			printf ("done.\n");

			c = locateCraftByDISEntityID ( & subjectEntityID );
			if ( c ) {

				stealthCraft ( c, u, -1, 1);

			}
		}

	}
	else {

		if ( end_game ) {
			fprintf ( stderr, "The -end-game switch is only valid when used " );
			fprintf ( stderr, "with stealth mode.\n" );
			fprintf ( stderr, "The switch will be ignored.\n\n" );
		}

		if ( subjectEntitySpecified ) {
			fprintf ( stderr, "The -subject-entity-id switch is only valid when used " );
			fprintf ( stderr, "with stealth mode.\n" );
			fprintf ( stderr, "The switch will be ignored.\n\n" );
		}

	}

	if ((u->dpy = XOpenDisplay(display)) == (Display *) NULL) {
		free((char *) u);
		cf->type = CT_FREE;
		sprintf(err, "%s: can't open %s\n", ACM, display);
		write(s, err, strlen(err));
		return -1;
	}
	screen = DefaultScreen(u->dpy);

	/*
	 *  Add viewer to list
	 */

	addViewer ( u );
	u->c = cf;

	if (radarfont)
		fontName = radarfont;
	else if ((fontName = XGetDefault(u->dpy, ACM, ARG_RADAR_FONT)) == NULL) {
		fontName = DEFAULT_RADAR_FONT;
	}
	if ((u->rfont = XLoadQueryFont(u->dpy, fontName)) == NULL) {
		XCloseDisplay(u->dpy);
		free((char *) u);
		cf->type = CT_FREE;
		sprintf(err, "%s: display %s doesn't know font %s\n",
				ACM, display, fontName);
		write(s, err, strlen(err));
		return -1;
	}

/*
 *  If the player has specified that the want the default Visual, simply
 *  give 'em that along with the default Colormap.
 */

	if (useDefaultVisual) {

		theVisual = DefaultVisual(u->dpy, screen);
		cmap = DefaultColormap(u->dpy, screen);
		depth = DisplayPlanes(u->dpy, screen);

	}

/*
 *  Look for a visual; if we can't find one,
 *  fall back to monochrome mode.
 */

	else {
		if ((theVisual = get_pseudo_visual(u->dpy, screen, &depth)) == NULL) {
			theVisual = DefaultVisual(u->dpy, screen);
			cmap = DefaultColormap(u->dpy, screen);
			depth = DefaultDepth(u->dpy, screen);
		}
		else {
			cmap = XCreateColormap(u->dpy, RootWindow(u->dpy, screen),
								   theVisual, AllocNone);
		}
	}

/*
 * Select colors for the border, the window background, and the
 * foreground.  We use the default colormap to allocate the colors in.
 */

	if (background == NULL)
		background = DEFAULT_BACKGROUND;

	if ((tempstr = XGetDefault(u->dpy, ACM, ARG_BORDER_COLOR)) == NULL)
		tempstr = DEFAULT_BORDER;
	if (XParseColor(u->dpy, cmap, tempstr, &color) == 0) {
		XCloseDisplay(u->dpy);
		free((char *) u);
		cf->type = CT_FREE;
		sprintf(err, "Can't get border color %s\n", tempstr);
		write(s, err, strlen(err));
		return -1;
	}

	if (depth == 1)
		bd = BlackPixel(u->dpy, screen);
	else {
		if (XAllocColor(u->dpy, cmap, &color) == 0) {
			XCloseDisplay(u->dpy);
			free((char *) u);
			cf->type = CT_FREE;
			sprintf(err, "Cannot allocate color cells\n");
			write(s, err, strlen(err));
			return -1;
		}
		bd = color.pixel;
	}

/*
 * Set the border width of the window, and the gap between the text
 * and the edge of the window, "pad".
 */

	pad = BORDER;
	if (borderWidth >= 0)
		bw = borderWidth;
	else if ((tempstr = XGetDefault(u->dpy, ACM, ARG_BORDER)) == NULL)
		bw = 1;
	else
		bw = atoi(tempstr);

/*
 * Deal with providing the window with an initial position & size.
 * Fill out the XSizeHints struct to inform the window manager.
 */

	if (geomSpec == NULL)
		geomSpec = XGetDefault(u->dpy, ACM, ARG_GEOMETRY);

/*
 * If the defaults database doesn't contain a specification of the
 * initial size & position, locate it in the center of the screen.
 */

	if (geomSpec == NULL) {
		xsh.flags = PPosition | PSize;
		xsh.height = FS_WINDOW_HEIGHT * DisplayWidth(u->dpy, screen) / 1280;
		xsh.width = FS_WINDOW_WIDTH * DisplayWidth(u->dpy, screen) / 1280;
		xsh.x = (DisplayWidth(u->dpy, screen) - xsh.width) / 2;
		xsh.y = (DisplayHeight(u->dpy, screen) - xsh.height) / 2;
	}
	else {
		int       bitmask;

		bzero((char *) &xsh, sizeof(xsh));
		bitmask = XParseGeometry(geomSpec, &xsh.x, &xsh.y,
								 &xsh.width, &xsh.height);
		if (bitmask & (XValue | YValue)) {
			xsh.flags |= USPosition;
		}
		if (bitmask & (WidthValue | HeightValue)) {
			xsh.flags |= USSize;
		}
	}

	width = xsh.width;
	height = xsh.height;

/*
 * Create the Window with the information in the XSizeHints, the
 * border width,  and the border & background pixels.
 */

	win_attr_mask = CWColormap | CWBitGravity | CWBackPixel;
	window_attributes.colormap = cmap;
	window_attributes.bit_gravity = NorthWestGravity;
	window_attributes.background_pixel = BlackPixel(u->dpy, screen);

	u->win = XCreateWindow(u->dpy, RootWindow(u->dpy, screen),
						   xsh.x, xsh.y, xsh.width, xsh.height, bw, depth,
			  InputOutput, theVisual, win_attr_mask, &window_attributes);

	scale = (double) xsh.width / (double) FS_WINDOW_WIDTH;

/*
 * Create a pixmap of the engine RPM gauge and flap indicators.
 */

	u->eng = XCreateBitmapFromData(u->dpy, u->win, eng_bits, eng_width,
								   eng_height);
	u->flap[0] = XCreateBitmapFromData(u->dpy, u->win, flaps0_bits,
									   flaps0_width, flaps0_height);
	u->flap[1] = XCreateBitmapFromData(u->dpy, u->win, flaps1_bits,
									   flaps1_width, flaps1_height);
	u->flap[2] = XCreateBitmapFromData(u->dpy, u->win, flaps2_bits,
									   flaps2_width, flaps2_height);
	u->flap[3] = XCreateBitmapFromData(u->dpy, u->win, flaps3_bits,
									   flaps3_width, flaps3_height);
	u->handle[0] = XCreateBitmapFromData(u->dpy, u->win, handleUp_bits,
										 handleUp_width, handleUp_height);
	u->handle[1] = XCreateBitmapFromData(u->dpy, u->win, handleDn_bits,
										 handleDn_width, handleDn_height);
	u->gearLight[0] = XCreateBitmapFromData(u->dpy, u->win, gearUp_bits,
											gearUp_width, gearUp_height);
	u->gearLight[1] = XCreateBitmapFromData(u->dpy, u->win, gearTran_bits,
										gearTran_width, gearTran_height);
	u->gearLight[2] = XCreateBitmapFromData(u->dpy, u->win, gearDown_bits,
										gearDown_width, gearDown_height);

/*
 * Set the standard properties and hints for the window managers.
 */

	XSetStandardProperties(u->dpy, u->win, ACM, ACM, None, argv, argc, &xsh);
	xwmh.flags = InputHint | StateHint;
	xwmh.input = True;
	xwmh.initial_state = NormalState;
	XSetWMHints(u->dpy, u->win, &xwmh);
	cursor = XCreateFontCursor(u->dpy, XC_tcross);
	XDefineCursor(u->dpy, u->win, cursor);

/*
 *  Tell the window manager that we'd like to participate in the
 *  WM_CLOSEDOWN and WM_DELETE_WINDOW protocols.
 */

	u->protocolsAtom = XInternAtom(u->dpy, "WM_PROTOCOLS", False);
	atom[0] =
		u->closedownAtom = XInternAtom(u->dpy, "WM_CLOSEDOWN", False);
	atom[1] =
		u->deleteWindowAtom = XInternAtom(u->dpy, "WM_DELETE_WINDOW", False);
	XSetWMProtocols(u->dpy, u->win, atom, 2);

/*
 *  Fill-in the viewer structure
 */
	XSelectInput(u->dpy, u->win, KeyPressMask | ButtonPressMask |
				 StructureNotifyMask | ButtonReleaseMask | ExposureMask);
	cf->team = team;

	cf->vl = u;
	strncpy(cf->name, logname, sizeof(cf->name));
	strncpy(cf->display, display, sizeof(cf->display));
	u->next = (viewer *) NULL;

/*
 *  We use the drawController to optimize areas that we'll
 *  be drawing using X, as well.
 */
	initializeDrawnItemController(&u->drawControl);
	addDrawnItem(&u->drawControl, &u->rpmState);
	addDrawnItem(&u->drawControl, &u->flapState);
	addDrawnItem(&u->drawControl, &u->fuelState);
	addDrawnItem(&u->drawControl, &u->consumpState);
	addDrawnItem(&u->drawControl, &u->gearState);
	addDrawnItem(&u->drawControl, &u->annunciatorState);
	addDrawnItem(&u->drawControl, &u->radarState);
	redrawAllItems(&u->drawControl);

/*
 *  Connect to the audio server
 */

	if (initializeAudio(cf, u, display) != 0) {
		sprintf(err, "Sound is not available on workstation %s\n", display);
		write(s, err, strlen(err));
	}

#ifdef USE_PIXMAP_ANIMATION
	if (depth != 1)
		_VDefaultWorkContext->usePixmaps = 1;
#endif

	_VDefaultWorkContext->usePixmaps = mono ? 1 : 0;

	u->v = VOpenViewport(u->dpy, screen, u->win, cmap, theVisual,
						 UNITS_METERS, scale, FEETtoMETERS(0.50),
						 width * VIEW_WINDOW_WIDTH / FS_WINDOW_WIDTH,
						 height * VIEW_WINDOW_HEIGHT / FS_WINDOW_HEIGHT);

/*
 *  If this is a color-rich visual, then enable depth-cueing mode
 */

	if (depth >= 6) {
		ViewportSetDepthCueing(u->v, 1);
	}

	if (VBindColors(u->v, background) < 0) {
		XCloseDisplay(u->dpy);
		free((char *) u);
		ptbl[player].type = CT_FREE;
		sprintf(err, "Error in binding colors.\n");
		write(s, err, strlen(err));
		return -1;
	}

	whitePixel = whiteColor->cIndex;
	blackPixel = blackColor->cIndex;
	grayPixel = grayColor->cIndex;
	HUDPixel = HUDColor->cIndex;

/*
 * Create the GC for drawing the picture.
 */

	gcv.graphics_exposures = False;
	gcv.font = u->rfont->fid;
	u->gc = XCreateGC(u->dpy, u->win, GCGraphicsExposures | GCFont, &gcv);

	gcv.graphics_exposures = False;
	gcv.foreground = u->v->flags & VPMono ?
		WhitePixel(u->v->dpy, u->v->screen) :
		VConstantColor(u->v, whitePixel);
	gcv.background = u->v->flags & VPMono ?
		BlackPixel(u->v->dpy, u->v->screen) :
		VConstantColor(u->v, blackPixel);
	gcv.line_width = 2;
	gcv.font = u->rfont->fid;
	u->gauge_gc = XCreateGC(u->dpy, u->win,
					  GCGraphicsExposures | GCForeground | GCBackground |
							GCLineWidth | GCFont, &gcv);


	resizePlayerWindow(cf, u, width, height, 1);

/*
 * Map the window to make it visible.
 */

	XMapWindow(u->dpy, u->win);
	if (depth != 1) {
#ifdef notdef
		register int curPixel;

		VExposeBuffer(u->v, u->gc);
		curPixel = *(u->v->pixel);
		XSetForeground(u->dpy, u->gc, curPixel);
		XFillRectangle(u->dpy, u->win, u->gc, 0, 0, u->width, u->height);
#endif
	}

	if (depth == 1)
		XSetWindowBackground(u->dpy, u->win, BlackPixel(u->dpy, screen));
	else
		XSetWindowBackground(u->dpy, u->win, VConstantColor(u->v, blackPixel));

	u->z.color = u->v->pixel[HUDPixel];
	u->z.depth = 1;

	u->rz.color = u->v->pixel[radarColor->cIndex];
	u->rz.depth = 2;

	++ptblCount;

	/*
	 * Take appropriate actions based on any overrides specified by the user
	 */

	if (overrides[SW_LATITUDE] || 
		overrides[SW_LONGITUDE] ||
		overrides[SW_ALTITUDE]) {

		if (overrides[SW_LATITUDE]) {
			cf->w.latitude = overrideLatitude;
		}

		if (overrides[SW_LONGITUDE]) {
			cf->w.longitude = overrideLongitude;
		}

		if (overrides[SW_ALTITUDE]) {
			cf->w.z = overrideAltitude;
		}
		else {
			cf->w.z = 0.0;
		}
		
		DISWorldCoordinatesToGeocentric(&cf->w,
										(dis_world_coordinates *) & cf->Sg);

		if (overrides[SW_ALTITUDE] == 0) {
			cf->w.z = localAltitude( &cf->Sg, &cf->w ) +
				FEETtoMETERS( cf->cinfo->groundingPoint.z );
			DISWorldCoordinatesToGeocentric(&cf->w,
											(dis_world_coordinates *) &cf->Sg);
		}

		cf->prevw = cf->w;
		cf->prevSg = cf->Sg;

		GenerateWorldToLocalMatrix(&cf->w, &cf->XYZtoNED);
	}

	if (overrides[SW_HEADING]) {

		cf->curHeading = overrideHeading_rad;

		buildEulerMatrix(cf->curRoll, cf->curPitch, cf->curHeading,
						 &(cf->trihedral));
	}

	if (overrides[SW_AIRSPEED_KTS]) {
		VPoint v = { overrideAirspeed_fps, 0.0, 0.0 };
		VTransform_ ( &v, &cf->trihedral, &cf->Cg );
	}

	/*
	 *  Transmit initial DIS entity state PDU
	 */
	
#ifdef HAVE_DIS
	if ( disInUse && cf->type != CT_DIS_STEALTH ) {

		DISWorldCoordinatesToGeocentric(&cf->w,
								  (dis_world_coordinates *) disLocation);
		disZeroVec[0] = 0.0;
		disZeroVec[1] = 0.0;
		disZeroVec[2] = 0.0;
		disOrientation[0] = cf->curHeading;
		disOrientation[1] = 0.0;
		disOrientation[2] = 0.0;

		dis_entityEnter(team, cf,
						&cf->cinfo->entityType,
						&cf->cinfo->altEntityType,
						disLocation, disZeroVec, disZeroVec,
						disOrientation, disZeroVec, &cf->disId);
	}
#endif

	return 0;

}

#ifndef WIN32

/*
 *  Until we have a "you're dead" dialog box in the UNIX version of ACM, 
 *  this will have to do ...
 */

int
killPlayerEx(craft * c,...)
{
	return killPlayer(c);
}
#endif

int
killPlayer(craft * c)
{

	viewer   *v, *vn;
	int       i;
	VPoint	 vel = { 0, 0, 0 };

	playSound(c, SoundCrash);

/*
 *  Decrement the player count, iff this is a real person that just got
 *  killed.
 */

	if (c->type == CT_PLANE && (c->flags & FL_BLACK_BOX) == 0) {
		--ptblCount;
	}

/*
 *  Erase our radar emissions
 */

	for (i = 0; i < MAXPLAYERS; ++i)
		ptbl[i].rval[c->pIndex] = 0.0;

/*
 *  Replace the plane with an explosion.
 */

	newExplosion(&(c->Sg), &vel, 30.0, 15.0, 4.0);

/*
 *  Free flight director storage
 */

#ifdef AFDS
	AFDSFree( c );

	if (c->fp) {
		freeFlightPlan ( c->fp );
	}
#endif

/*
 *  Free HUD string storage
 */

	if (c->leftHUD[0] != (char *) NULL)
		for (i = 0; i < 6; ++i) {
			free(c->leftHUD[i]);
			free(c->rightHUD[i]);
		}

/*
 *  Close viewers' display
 */

	for (v = c->vl; v != (viewer *) NULL;) {

		/*
		 *  If this was a situation where we had grabbed control in steath
		 *  mode and then died, return to the browsing state.
		 */

		if ( v->viewer_state == ViewerStateNormal && 
			 v->watchedCraft != NULL &&
			 v->watchedCraft->type == CT_DIS_STEALTH ) {
			printf ("player killed: returning to stealth browsing mode\n");
			v->viewer_state = ViewerStateBrowsing;
			v->c = v->watchedCraft;
			v->watchedCraft = NULL;
			v = NULL;
		}
		else {

			/*
			 *  remove viewer from viewer list
			 */

			if (removeViewer ( v )) {
				printf ("unable to locate viewer in viewer list\n");
			}

			/*
			 *  shutdown the viewer
			 */

			VCloseViewport(v->v);
			XCloseDisplay(v->dpy);
			shutdownAudio(c, v);
			vn = (viewer *) v->next;
			free((char *) v);
			v = NULL;
		}
	}

	if (c->flags & FL_RECORD) {
		--recordCount;
	}

	if (c->flags & FL_BLACK_BOX)
		blackBoxKillPlayer(c->pIndex);

#ifdef HAVE_DIS
	if (c->type == CT_PLANE || c->type == CT_DRONE) {
		/* active (broadcasting) player, announce its death */
#ifdef DIS_DEBUG
		printf("Killed local player/drone: %d %d\n", c->pIndex, c->disId);
#endif
		dis_entityExit(c->disId);
	}
#endif

	c->type = CT_FREE;
	return 0;
}
