/*  damedit	Edit damage profile files for
		MicroSoft Combat Flight Simulator (MSCFS)
 

    V1.1
    Bug fixes:

    When systems were deleted, surplus effects showed up in saved
    file: Fixed

    When systems or boxes were deleted, boxmaps were not shifted
    accordingly: Fixed

    Top view mirrored, so starboard wing shown to left: Fixed; also
    end (Z) view mirrored, so it is now rear view.

    FLAK class did not exist and CANNON class misnamed FLAK: Fixed

    Trigger selection did not work properly: Fixed

    Directory list got messed up after use: New file selction style.

    Various arrays etc. were not cleared efficiently, so program had
    to be restarted if a new file were to be loaded: Fixed

    Some DP other DP editors leave a lot of blank space in the file;
    this confused the parse function: Fixed; damedit will now strip
    any surplus blanks.

    Changes:

    File selection changed to scroll window. Separate selection of
    .dp file, so .dp with other prefix than aircraft dir name and
    multiple .dp can be handled.

    Number of elements reduced, as they proved to be far above what
    was used in practice. Now the following numbers of elements
    are supported:

    Boxes: 20
    Systems: 50
    Effects (pr. system) 5
    Gunstations: 16
    Guns (pr. gunstation): 8

    New features:

    Harmonize function now includes a user-definable target point.
    Note: The harmonize function only works well with small heading
    angles (X axis).

    Box locator function saves a file with a gunflash in each corner
    of selected box.

    V1.2  Filename can now be entered on command line. This means that
    .dp can be mapped to Damedit (via a shortcut to get full-screen mode).
    This was in recognition of the fact that the file selection function
    was not very user-friendly.

    Rating system from RATEDP implemented.

    V1.3  Now 64 gunstations. Various file variables made 255 long.

*/


/* general definitions */
#define FIELDS 210
#define DISPNUM 20

/* colors */
#define LGRAY 7
#define DGRAY 8
#define LBLUE 9
#define LGREEN 10
#define LCYAN 11
#define LRED 12
#define LMAGENTA 13

/* GUI fields, parms and types */
#define AUTO -1
#define REPEAT -2
#define CENTER -3
#define BLOCK_1 -4
#define BLOCK_2 -5
#define BLOCK_3 -6
#define BLOCK_4 -7
#define BLOCK_5 -8
#define BLOCK_6 -9
#define BLOCK_7 -10
#define BLOCK_8 -11
#define SPACED -12
#define BUTTON 0
#define TEXT_FIELD 1
#define FREE_TEXT 2
#define RADIO 3
#define MONOSTABLE 4
#define ICON 5
#define MICON 6

/* Damage editor parameters */

#define NAME 80
#define BOXES 20
#define SYSTEMS 50
#define EFFECTS 5
#define GST 64
#define GUNS 8
#define EXTRAS 8
#define RADIANS 0.01745329252
#define DEGREES 57.29577951

#include <dos.h>
#include <graphics.h>
#include <stdio.h>
#include <math.h>
#include <signal.h>
#include <dir.h>

/* Various global variables etc. */
union REGS regs;
char arrow1[80]="m0,-2d2,0d1,0d1,2d-1,2d-1,0d-2,0d0,-2f0,0";  /* Arrow symbol (up)*/
char cross[80]="m0,-1d-1,-2d-2,-1d-1,0d-2,1d-1,2d0,1d1,2d2,1d1,0d2,-1d1,-2d0,-1f0,0"; /* cross symbol */

/* Boxes structure */
struct {char name[NAME]; double xlo, ylo, zlo, xhi, yhi, zhi;} boxes[BOXES];
int boxptr, boxmax=0;

/* Systems structure */
struct {char name[NAME]; double lifepoints; int sysid;} systems[SYSTEMS];
int sysptr, sysmax=0;

/* Boxmap array */
int boxmaps[BOXES][SYSTEMS];

/* Effects structure */
struct {int level[EFFECTS]; char code[EFFECTS][20]; int smoke[EFFECTS], amount[EFFECTS];} effects[SYSTEMS];
int effptr;

/* Gunstations structure */
struct {int type, sysid, trigger; double rate, velocity, alive, flash; int range, sound, tracer; char dice[8]; int damage; double xpos, ypos, zpos, pitch, bank, heading, leftangle, rightangle, upangle, downangle, weight;} gst[GST];
int gstptr, gstmax=0, gstparmptr=0;

/* Guns structure */
struct {int maxammo[GUNS]; double xoff[GUNS], yoff[GUNS], zoff[GUNS], pitch[GUNS], heading[GUNS]; int tracer[GUNS], defammo[GUNS];} guns[GST];
int gunptr;

/* Extras structure */
struct {int type, smoketype, smokecolor; double x, y, z;} extras[EXTRAS];
int extraptr, extramax=-1;

/* Extras strings */
char *smoketype[]={"None", "Timed", "Repeating", "Constant", "Random",};
char *smokecolor[]={"Fire", "White", "Black", "Red", "Green", "Blue",};

struct {double x, y, z;} sightpoint1 = {0, 0.5, 300};
struct {double x, y, z;} sightpoint2 = {0, 1, 500};
struct {double x, y, z;} sightpoint3 = {0, 2, 1000};
char sightx[10]="0", sighty[10]="2", sightz[10]="1000";

/* GUI variables etc */
int height, maxx, maxy, latch1, iflag=0;
double zoom=0.5, panx=0, pany=0;
char dispzoom[10]="1";
char status[79];

char ifile[255]="(no plane specified)";
char ofile[255]="(no plane specified)";
char airplane[255]="(specify)";
char dpfile[255]="(specify)";
char gamepath[255]=".\\aircraft\\";
int fileptr=2;
int dpptr=0;
int axis='Y';

int page=1; /* page works bitwise, thus 1=page1, 2=page 2, 3=page 1 AND 2,
		 0=disabled, etc. That means that an object can be visible
		 on more than one page, and more than one page can be visi-
		 ble at one time  */

/* fields structure:
   name=id string; makes it possible to allocate a function to a field
       independently of other properties.
   text=text to appear in field.
   color=text color.
   x, y, width, height= pos. and size of field, can either be a positive
      value, which is interpreted as character heights or a negative value,
      which will be interpreted in the following ways:
      value	alias	effect
      -1        AUTO	x, y: Next position right of or below (resp.) of previous field
			width, height: Adjust to string in text.
      -2	REPEAT  x, y: Same position as previously defined field.
			width, height: Same value as previous field.
      -3	CENTER	x, y: Center on screen.
			width, height: No effect.
      -4        BLOCK_1 x, y: Absolute positions, screen is divided into 8x8 blocks; place field in block.
       :          :     width, height: fit field into block(s): BLOCK_1 one block, BLOCK_2 two blocks, etc.
      -11       BLOCK_8

      -12	SPACED	x, y: Like AUTO, but extra space inserted
			width, height: No effect.

      Interpretation of these values happen once at start-up, where the
      FieldAdjust function will replace them with absolute pixel counts. Accessing the variables
      later during execution will yield pixel values. Note that this adjustment works across page
      boundaries.

   type=type of field, mode field properties depending on type.
     Current types and modes:

     value	alias		function		modes
     0          BUTTON          Bi-stable button        0: released, 1 depressed
     1		TEXT_FIELD	Framed text		Value of mode determines background color
     2		FREE_TEXT	Free text		Irrelevant
     3		RADIO           Radio button            0: released, 1 depressed. Only one of
							the radio buttons with the same first
							char of name can be depressed at any time.
     4		MONOSTABLE      Monostable button       0: released, 1 depressed.
     5          ICON            Icon button             Functions as the BUTTON type, but the text
							string is used as control string to the draw
							function in order to draw a symbol on it.
							The AUTO width and hight will not work here,
							you must ensure that the button will hold the
							symbol. The color parameter will determine
							the draw color till a color command is encoun-
							tered in the control string.
     6		MICON		Icon monostable		Icon monostable button,


     Notes:  Fields inside other fields must be defined after the enclosing field(s).
   */

/* Structure with all fields */
struct
{char 	*name, 		*text; 					int color, 	x, 	y, 	width, 	height, 	type, 		mode, 	page;} fields[FIELDS] = {
/* Header */
{	"title",	"Damage profile editor V1.3, by Hans Egebo", BLUE,	BLOCK_1,AUTO,	BLOCK_8,2,		TEXT_FIELD,	CYAN,	255},
{	"Page1",	"Main page",				LRED,		BLOCK_1,AUTO,	BLOCK_1,2,                   RADIO,         1,  255},
{	"Page2",	"Systems",				LRED,		AUTO,	REPEAT,	BLOCK_1,2,                   RADIO,         0,  255},
{	"Page3",	"Gunstat.",				LRED,		AUTO,	REPEAT,	BLOCK_1,2,                   RADIO,         0,  255},
{	"Page4",	"Guns",					LRED,		AUTO,	REPEAT,	BLOCK_1,2,                   RADIO,         0,  255},
{	"Page5",	"Boxes",				LRED,		AUTO,	REPEAT,	BLOCK_1,2,                   RADIO,         0,  255},
{	"Page7",	"Boxmaps",				LRED,		AUTO,	REPEAT,	BLOCK_1,2,                   RADIO,         0,  255},
{	"Page6",	"Disabled",				LRED,		AUTO,	REPEAT,	      0,2,                   RADIO,         0,  0},
{	"Page8",	"Extras",				LRED,		AUTO,	REPEAT,	BLOCK_1,2,                   RADIO,         0,  255},
/* Main Page (1) */
{	"t",		"Airplane Type:",			YELLOW,		BLOCK_1,     6,	BLOCK_2,2,		TEXT_FIELD,	DGRAY,	1},
{	"t",		"DP filename:",				YELLOW,		BLOCK_3,     6,	BLOCK_2,2,		TEXT_FIELD,	DGRAY,	1},
{	"iplane",	 airplane,				WHITE,		BLOCK_1,  AUTO,	      14,2,		TEXT_FIELD,	BLUE,	1},
{	"Drup",		arrow1,					LRED,		AUTO,	REPEAT,	2,	2,		MICON,		0,	1},
{	"Drdown",	arrow1,					LRED,		AUTO,	REPEAT,	2,	2,		MICON,		180,	1},
{	"idpfile",	 dpfile,				WHITE,		BLOCK_3,REPEAT,	      14,2,		TEXT_FIELD,	BLUE,	1},
{	"Dpup",		arrow1,					LRED,		AUTO,	REPEAT,	2,	2,		MICON,		0,	1},
{	"Dpdown",	arrow1,					LRED,		AUTO,	REPEAT,	2,	2,		MICON,		180,	1},
{	"Load",		"Load",					LRED,		BLOCK_1,  AUTO,	BLOCK_1,2,		MONOSTABLE,	0,	1},
{	"Save",		"Save",					LRED,		AUTO,	REPEAT,	BLOCK_1,2,		MONOSTABLE,	0,	1},
{	"loadstat",	"No file loaded",			WHITE,          AUTO,   REPEAT, BLOCK_2,2,              TEXT_FIELD,     BLACK,  1},
{	"t",		"File path",				YELLOW,		BLOCK_1,SPACED,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	1},
{	"idir",		gamepath,				LGREEN,         AUTO,   REPEAT, BLOCK_4,2,              TEXT_FIELD,     BLACK,  1},
{       "t",            "Defensive rating:",                    YELLOW,         BLOCK_1,BLOCK_4,BLOCK_2,2,              TEXT_FIELD,     DGRAY,  1},
{       "rate1",        "         ",                            LGREEN,         AUTO,   REPEAT, BLOCK_1,2,              TEXT_FIELD,     BLACK,  1},
{       "t",            "Offensive rating:",                    YELLOW,         BLOCK_1,AUTO,BLOCK_2,2,              TEXT_FIELD,     DGRAY,  1},
{       "rate2",        "         ",                            LGREEN,         AUTO,   REPEAT, BLOCK_1,2,              TEXT_FIELD,     BLACK,  1},
{	"quit",		"QUIT",					YELLOW,		BLOCK_1,BLOCK_8,BLOCK_1,2,		BUTTON,		0,	1},

/* Systems page (2) */
{	"t",		"No.:",					YELLOW,		BLOCK_1,      6,BLOCK_1,2,		TEXT_FIELD,	DGRAY,	66},
{       "Ysysno",	"        ",				LGREEN,		REPEAT,	AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLACK,	66},
{	"t",		"System name",				YELLOW,         AUTO,	      6,BLOCK_3,2,		TEXT_FIELD,	DGRAY,	66},
{	"Ysysname",	"                             ",	WHITE,		REPEAT,	AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLUE,	66},
{	"t",		"Lifepts.",				YELLOW,		AUTO,	6,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	66},
{	"Ysyslife",	"        ",				WHITE,		REPEAT,	AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	66},
{	"t",		"Sys. id.",				YELLOW,		AUTO,	6,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	66},
{	"Ysysid",	"        ",				WHITE,		REPEAT,	AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	66},
{	"Ysysup",	arrow1,					LRED,		AUTO,	6,	2,	2,              MICON,		0,	66},
{	"Ysysdown",	arrow1,					LRED,		REPEAT,	AUTO,	2,	2,		MICON,		180,	66},
{	"Ysysdelete",	"Delete",                               LMAGENTA,	SPACED,	6,	BLOCK_1,2,		MONOSTABLE,	0,	2},
{	"Ysysinsert",	"Insert",                               LRED,		REPEAT,	AUTO,	BLOCK_1,2,		MONOSTABLE,	0,	2},
{	"t",		"Effects for this system:",		YELLOW,		BLOCK_1,BLOCK_3,BLOCK_3,2,		TEXT_FIELD,	DGRAY,	2},
{	"t",		"No.:",					YELLOW,		BLOCK_1,AUTO,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	2},
{	"t",		"%level",				YELLOW,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	2},
{	"t",		"Code",					YELLOW,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	2},
{	"t",		"Effect",				YELLOW,		AUTO,	REPEAT,	BLOCK_2,2,              TEXT_FIELD,	DGRAY,	2},
{	"t",		"Severity",				YELLOW,		AUTO,	REPEAT,	BLOCK_2,2,		TEXT_FIELD,	DGRAY,	2},
{	"Effno0",	"        ",				LGREEN,		BLOCK_1,AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLACK,	2},
{	"Efflev0",	"        ",				WHITE,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	2},
{	"Effcode0",	"        ",				WHITE,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	2},
{	"Effcol0",	"             ",			WHITE,		AUTO,	REPEAT,	BLOCK_2,2,              TEXT_FIELD,	BLUE,	2},
{	"Effamount0",	"             ",			WHITE,		AUTO,	REPEAT,	BLOCK_2,2,		TEXT_FIELD,	BLUE,	2},
{	"Effno1",	"        ",				LGREEN,		BLOCK_1,AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLACK,	2},
{	"Efflev1",	"        ",				WHITE,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	2},
{	"Effcode1",	"        ",				WHITE,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	2},
{	"Effcol1",	"             ",			WHITE,		AUTO,	REPEAT,	BLOCK_2,2,              TEXT_FIELD,	BLUE,	2},
{	"Effamount1",	"             ",			WHITE,		AUTO,	REPEAT,	BLOCK_2,2,		TEXT_FIELD,	BLUE,	2},
{	"Effno2",	"        ",				LGREEN,		BLOCK_1,AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLACK,	2},
{	"Efflev2",	"        ",				WHITE,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	2},
{	"Effcode2",	"        ",				WHITE,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	2},
{	"Effcol2",	"             ",			WHITE,		AUTO,	REPEAT,	BLOCK_2,2,              TEXT_FIELD,	BLUE,	2},
{	"Effamount2",	"             ",			WHITE,		AUTO,	REPEAT,	BLOCK_2,2,		TEXT_FIELD,	BLUE,	2},
{	"Effno3",	"        ",				LGREEN,		BLOCK_1,AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLACK,	2},
{	"Efflev3",	"        ",				WHITE,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	2},
{	"Effcode3",	"        ",				WHITE,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	2},
{	"Effcol3",	"             ",			WHITE,		AUTO,	REPEAT,	BLOCK_2,2,              TEXT_FIELD,	BLUE,	2},
{	"Effamount3",	"             ",			WHITE,		AUTO,	REPEAT,	BLOCK_2,2,		TEXT_FIELD,	BLUE,	2},
{	"Effno4",	"        ",				LGREEN,		BLOCK_1,AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLACK,	2},
{	"Efflev4",	"        ",				WHITE,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	2},
{	"Effcode4",	"        ",				WHITE,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	2},
{	"Effcol4",	"             ",			WHITE,		AUTO,	REPEAT,	BLOCK_2,2,              TEXT_FIELD,	BLUE,	2},
{	"Effamount4",	"             ",			WHITE,		AUTO,	REPEAT,	BLOCK_2,2,		TEXT_FIELD,	BLUE,	2},
/* Gunstations page (4): */
{	"t",		"Gunstation no.:",			YELLOW,		BLOCK_1,     6, BLOCK_2,2,		TEXT_FIELD,	DGRAY,	4},
{	"Gunstnum",	"     ",				LGREEN,		REPEAT,	AUTO,	REPEAT,	2,		TEXT_FIELD,	BLACK,	4},
{	"Gunstup",	arrow1,					LRED,		AUTO,	     6, 2,	2,		MICON,		0,	4},
{	"Gunstdown",	arrow1,					LRED,		REPEAT,	AUTO,	2,	2,		MICON,		180,	4},
{	"t",		"Parameter name:",			YELLOW,		SPACED,      6,	BLOCK_2,2,		TEXT_FIELD,	DGRAY,	4},
{	"Gunstparmnam",	"               ",			LGREEN,		REPEAT,	AUTO,	BLOCK_2,2,		TEXT_FIELD,	BLACK,	4},
{	"t",		"Value:",				YELLOW,		AUTO,	     6,	BLOCK_3,2,		TEXT_FIELD,	DGRAY,	4},
{	"Gunstparmval",	"                            ",		LGREEN,		REPEAT,	AUTO,	REPEAT,	2,		TEXT_FIELD,	BLACK,	4},
{	"Gunstpup",	arrow1,					LRED,		AUTO,	    6,  2,	2,		MICON,		0,	4},
{	"Gunstpdown",	arrow1,					LRED,		REPEAT,	AUTO,	2,	2,		MICON,		180,	4},
{	"Gunstped1",	arrow1,					LRED,		BLOCK_8,    6,  1,	1,		MICON,		0,	4},
{	"Gunstped2",	arrow1,					LRED,		REPEAT,	    7,	1,	1,		MICON,		180,	4},
{	"Gunstdelete",	"Delete gunstation",                    LMAGENTA,	BLOCK_1,    12, BLOCK_2,2,		MONOSTABLE,	0,	4},
{	"Gunstcopy",	"Copy gunstation",                      LRED,		BLOCK_1,AUTO, BLOCK_2,2,		MONOSTABLE,	0,	4},
{	"t",		"Harmonize",	                        YELLOW,		BLOCK_1,SPACED, BLOCK_2,2,		TEXT_FIELD,	DGRAY,	4},
{	"Gunstaim1",	"300m",		                        LRED,		BLOCK_1, AUTO, BLOCK_1,2,		MONOSTABLE,	0,	4},
{	"Gunstaim2",	"500m",		                        LRED,		AUTO,	REPEAT,BLOCK_1,2,		MONOSTABLE,	0,	4},
{	"Gunstaim3",	"User",		                        LRED,		BLOCK_1, AUTO,BLOCK_1,2,		MONOSTABLE,	0,	4},
{	"t",		"Set X:",				YELLOW,		BLOCK_1, AUTO,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	12},
{	"Gsightx",	 sightx,				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	12},
{	"t",		"Set Y:",				YELLOW,		BLOCK_1, AUTO,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	12},
{	"Gsighty",	 sighty,				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	12},
{	"t",		"Set Z:",				YELLOW,		BLOCK_1, AUTO,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	12},
{	"Gsightz",	 sightz,				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	12},


/* Guns page (8): */
{	"t",		"Gunstation no.:",			YELLOW,		BLOCK_1,    6,	BLOCK_2,2,		TEXT_FIELD,	DGRAY,	8},
{	"gunstnum",	"      ",				LGREEN,		AUTO,	REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLACK,	8},
{	"gunstnam",	"                            ",		LGREEN,		BLOCK_1, AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	8},
{	"t",		"Gun no.:",				YELLOW,		AUTO,	6,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	8},
{	"gunnum",	"       ",				LGREEN,		REPEAT, AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLACK,	8},
{	"gunup",	arrow1,					LRED,		AUTO,	6,	2,	2,		MICON,		0,	8},
{	"gundown",	arrow1,					LRED,		REPEAT,	AUTO,	2,	2,		MICON,		180,	8},
{	"t",		"Max ammo",				YELLOW,		BLOCK_6,    6,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	8},
{	"gunmaxammo",	"        ",				WHITE,		REPEAT,	AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	8},
{	"t",		"Def. ammo",				YELLOW,		AUTO,	6,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	8},
{	"gundefammo",	"        ",				WHITE,		REPEAT,	AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	8},
{	"t",		"Tracer",				YELLOW,		AUTO,	6,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	8},
{	"guntracer",	"   ",					WHITE,		REPEAT,	AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	8},
{	"gundelete",	"Delete gun",		                LMAGENTA,	BLOCK_1,    12, BLOCK_2,2,		MONOSTABLE,	0,	0},
{	"guncopy",	"Copy gun",	                        LRED,		BLOCK_1,AUTO, BLOCK_2,2,		MONOSTABLE,	0,	0},
{	"t",		"Harmonize",	                        YELLOW,		BLOCK_1,SPACED, BLOCK_2,2,		TEXT_FIELD,	DGRAY,	8},
{	"gunaim1",	"300m",		                        LRED,		BLOCK_1, AUTO, BLOCK_1,2,		MONOSTABLE,	0,	8},
{	"gunaim2",	"500m",		                        LRED,		AUTO,	REPEAT,BLOCK_1,2,		MONOSTABLE,	0,	8},
{	"gunaim3",	"User",		                        LRED,		BLOCK_1, AUTO,BLOCK_1,2,		MONOSTABLE,	0,	8},

/* Boxes page (16): */
{	"t",		"Box no:",				YELLOW,		BLOCK_1,     6, BLOCK_1,2,		TEXT_FIELD,	DGRAY,	16},
{	"t",		"Box Name:",				YELLOW,		AUTO,	REPEAT,	BLOCK_3,2,		TEXT_FIELD,	DGRAY,	16},
{	"Boxinsert",	"Insert new box",			LRED,		BLOCK_6,REPEAT, BLOCK_2,2,		MONOSTABLE,	0,	16},
{	"Boxdelete",	"Delete box",				LMAGENTA,       REPEAT,	AUTO,	BLOCK_2,2,	        MONOSTABLE,	0,	16},
{	"Boxnum",	"     ",				LGREEN,		BLOCK_1,     8, BLOCK_1,2,		TEXT_FIELD,	BLACK,	16},
{	"Boxname",	"                            ",		WHITE,		AUTO,	REPEAT,	BLOCK_3,2,		TEXT_FIELD,     BLUE,  16},
{	"Boxup",	arrow1,					LRED,		AUTO,	     6, 2,	2,		MICON,		0,	16},
{	"Boxdown",	arrow1,					LRED,		REPEAT,	     8,	2,	2,		MICON,		180,	16},
{	"t",		"From XYZ",		     		YELLOW,		BLOCK_1,    12,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	16},
{	"Boxxlo",	"          ",				WHITE,		BLOCK_1, AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	16},
{	"Boxylo",	"          ",				WHITE,		BLOCK_1, AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	16},
{	"Boxzlo",	"          ",				WHITE,		BLOCK_1, AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	16},
{	"t",		"To   XYZ",		     		YELLOW,		BLOCK_2,    12,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	16},
{	"Boxxhi",	"          ",				WHITE,		BLOCK_2, AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	16},
{	"Boxyhi",	"          ",				WHITE,		BLOCK_2, AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	16},
{	"Boxzhi",	"          ",				WHITE,		BLOCK_2, AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	16},
{	"Boxlocate",	"Locate this box",			LRED,		BLOCK_1, AUTO,BLOCK_2,2,              MONOSTABLE,     0,      16},

/* Boxmap page (64) */
{	"t",		"Box name",				YELLOW,		BLOCK_1,   12,	BLOCK_3,2,		TEXT_FIELD,	DGRAY,	64},
{	"t",		"Hit %",				YELLOW,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	64},
{	"t",		"Box name",				YELLOW,		AUTO,   12,	BLOCK_3,2,		TEXT_FIELD,	DGRAY,	64},
{	"t",		"Hit %",				YELLOW,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	64},
{	"bmname0",	boxes[0].name,				LGREEN,		BLOCK_1,AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval0",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname1",	boxes[1].name,				LGREEN,		AUTO,  REPEAT,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval1",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname2",	boxes[2].name,				LGREEN,		BLOCK_1,AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval2",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname3",	boxes[3].name,				LGREEN,		AUTO,  REPEAT,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval3",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname4",	boxes[4].name,				LGREEN,		BLOCK_1,AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval4",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname5",	boxes[5].name,				LGREEN,		AUTO,  REPEAT,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval5",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname6",	boxes[6].name,				LGREEN,		BLOCK_1,AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval6",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname7",	boxes[7].name,				LGREEN,		AUTO,  REPEAT,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval7",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname8",	boxes[8].name,				LGREEN,		BLOCK_1,AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval8",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname9",	boxes[9].name,				LGREEN,		AUTO,  REPEAT,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval9",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname10",	boxes[10].name,				LGREEN,		BLOCK_1,AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval10",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname11",	boxes[11].name,				LGREEN,		AUTO,  REPEAT,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval11",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname12",	boxes[12].name,				LGREEN,		BLOCK_1,AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval12",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname13",	boxes[13].name,				LGREEN,		AUTO,  REPEAT,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval13",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname14",	boxes[14].name,				LGREEN,		BLOCK_1,AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval14",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname15",	boxes[15].name,				LGREEN,		AUTO,  REPEAT,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval15",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname16",	boxes[16].name,				LGREEN,		BLOCK_1,AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval16",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname17",	boxes[17].name,				LGREEN,		AUTO,  REPEAT,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval17",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname18",	boxes[18].name,				LGREEN,		BLOCK_1,AUTO,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval18",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},
{	"bmname19",	boxes[19].name,				LGREEN,		AUTO,  REPEAT,	BLOCK_3,2,		TEXT_FIELD,	BLACK,	64},
{	"bmval19",	"       ",				WHITE,		AUTO,  REPEAT,	BLOCK_1,2,		TEXT_FIELD,	BLUE,	64},


/* Graphics display, pages 4, 8, 16, */
{	"t",		"Display",				YELLOW,		BLOCK_1, BLOCK_6,BLOCK_2,2,		TEXT_FIELD,	DGRAY,	156},
{	"t",		"View from:",				YELLOW,		BLOCK_1, AUTO,	AUTO,	2,		TEXT_FIELD,	DGRAY,	156},
{	"ViewX",	"X",					LRED,		AUTO,	REPEAT,	2,	2,		RADIO,          0,	156},
{	"ViewY",	"Y",					LRED,		AUTO,	REPEAT,	2,	2,		RADIO,          1,	156},
{	"ViewZ",	"Z",					LRED,		AUTO,	REPEAT,	2,	2,		RADIO,          0,	156},
{	"t",		"Grid:",				YELLOW,		BLOCK_1, AUTO,	BLOCK_1,2,		TEXT_FIELD,	DGRAY,	156},
{	"dispzoom",	dispzoom,				LGREEN,		BLOCK_1, AUTO,	BLOCK_1,2,		TEXT_FIELD,	BLACK,	156},
{	"zoomin",	arrow1,                         	LRED,		AUTO,	REPEAT,	2,	2,		MICON,		180,	156},
{	"Zoomout",	arrow1,                         	LRED,		AUTO,	REPEAT,	2,	2,		MICON,		0,	156},
{	"t",		"Pan",					YELLOW,		BLOCK_1,AUTO, BLOCK_1,2,		TEXT_FIELD,	DGRAY,	156},
{	"dummy",	"",					0,		2, 	AUTO,	2,	2,		FREE_TEXT,	DGRAY,	156},
{	"panup",	arrow1,					LRED,		AUTO,	REPEAT,	2,	2,		MICON,		0,	156},
{	"panleft",	arrow1,					LRED,		2,	AUTO,	2,	2,		MICON,		270,	156},
{	"pancent",	cross,					LRED,		AUTO,	REPEAT,	2,	2,		MICON,		0,	156},
{	"panright",	arrow1,					LRED,		AUTO,	REPEAT,	2,	2,		MICON,		90,	156},
{	"dummy",	"",					0,		2,	AUTO,	2,	2,		FREE_TEXT,	DGRAY,	156},
{	"pandown",	arrow1,					LRED,		AUTO,	REPEAT,	2,	2,		MICON,		180,	156},
{	"screen",	"",					BLACK,		BLOCK_3,    12,BLOCK_6,BLOCK_6,		TEXT_FIELD,	BLACK,	156},


/* Extras page (128): */
{	"t",         "Extra effect no.",			YELLOW, 	BLOCK_1,BLOCK_2, BLOCK_2,2,		TEXT_FIELD,	DGRAY,	128},
{	"enum",      " none   ",  			 	LGREEN, 	AUTO,	REPEAT,  BLOCK_1,2,		TEXT_FIELD,	BLACK,	128},
{	"enumup",       arrow1,                         	LRED,		AUTO,	REPEAT,	2,	2,		MICON,		180,	128},
{	"enumdn",	arrow1,                         	LRED,		AUTO,	REPEAT,	2,	2,		MICON,		0,	128},
{	"enew",	     "New",					LRED,		BLOCK_5,REPEAT, BLOCK_1,2,		MONOSTABLE,	0,	128},
{	"edel",	     "Delete",					LRED,		AUTO,REPEAT, BLOCK_1,2,		MONOSTABLE,	0,	128},
{	"t",         "Smoke",			 		YELLOW, 	BLOCK_1,SPACED,    BLOCK_2,2,		TEXT_FIELD,	DGRAY,	128},
{	"t",         "Type",				 	YELLOW, 	BLOCK_1,AUTO, 	 BLOCK_1,2,		TEXT_FIELD,	DGRAY,	128},
{	"esmoke",    " none   ",			 	WHITE,	 	AUTO,	REPEAT,  BLOCK_1,2,		TEXT_FIELD,	BLUE,	128},
{	"t",         "Color",				 	YELLOW, 	BLOCK_1,AUTO, 	 BLOCK_1,2,		TEXT_FIELD,	DGRAY,	128},
{	"ecolor",    " none   ",			 	WHITE,	 	AUTO,	REPEAT,  BLOCK_1,2,		TEXT_FIELD,	BLUE,	128},
{	"t",         "X-pos",				 	YELLOW, 	BLOCK_1,AUTO, 	 BLOCK_1,2,		TEXT_FIELD,	DGRAY,	128},
{	"expos",     " none   ",			 	WHITE,	 	AUTO,	REPEAT,  BLOCK_1,2,		TEXT_FIELD,	BLUE,	128},
{	"t",         "Y-pos",				 	YELLOW, 	BLOCK_1,AUTO, 	 BLOCK_1,2,		TEXT_FIELD,	DGRAY,	128},
{	"eypos",     " none   ",			 	WHITE,		AUTO,	REPEAT,  BLOCK_1,2,		TEXT_FIELD,	BLUE,	128},
{	"t",         "Z-pos",				 	YELLOW, 	BLOCK_1,AUTO, 	 BLOCK_1,2,		TEXT_FIELD,	DGRAY,	128},
{	"ezpos",     " none   ",			 	WHITE,	 	AUTO,	REPEAT,  BLOCK_1,2,		TEXT_FIELD,	BLUE,	128},
};

struct {int x, y, button;} mouse;

/* File command strings */
char coms[20][26]={"box.", "system.", "[BOXMAPS.", "boxmap.", "[EFFECTS.", "effect.", "gunstation.", "[guns.", "gun.", "\"system_name.", "[EXTRA.", "extra_type", "smoke_type", "smoke_color", "smoke_xoffset", "smoke_yoffset", "smoke_zoffset", "END_COMS",};

/* Gun setting strings */
char *guntype[]={"Undefined", "Gun", "Cannon", "Bomb/Rocket", "Flak",};
char *trigger[]={"Undefined", "Gun", "Cannon", "Gun+Cannon", "Bomb", "Gun+Bomb", "Cannon+Bomb", "GUN+Cannon+Bomb", "Rocket",};
char *gunsound[]={"-", "50 cal", "303 cal", "7.62mm", "20mm", "30mm", "13mm", "Bomb",};
char *effectcode[]={"DAMAGE", "LEAK", "BREAK", "BOMB",};
char *effect[]={"-", "White smoke", "Black smoke", "Fire",};
char *severity[]={"-", "Puff", "Intermittent", "Continuous",};
double lzoom;
struct ffblk ffblk;


void StatusLine(char *msg);
void changetextstyle(int font, int direction, int charsize);
void erase();
void drawfield(int i, int m);
void erasefield(int i, int m);

FILE  *fopen();
char *optarg;
int optind, optflag=0;


void atthand(void){
  cleardevice();        /* make a clean exit */
  closegraph();
  exit(2);
}

main(argc, argv)
int argc;
char **argv;

{
  char msg[80];
  int gdriver= DETECT, gmode, errorcode;
  int maxcolor;
  int c, cc, hflag=1, f=0, i, j;
  strcpy(msg, "");
  signal(SIGINT,atthand);
  initgraph(&gdriver, &gmode, "");
  errorcode=graphresult();
  if (errorcode != grOk)
  {
    printf("Graphics error: %d\n", errorcode);
    printf("Press any key to halt:");
    getch();
    exit(1);
  }
  changetextstyle( DEFAULT_FONT, HORIZ_DIR, 1 );
  settextjustify( CENTER_TEXT, TOP_TEXT );
  setlinestyle( SOLID_LINE, 0, NORM_WIDTH );
  height = textheight( "H" );
  maxx=getmaxx();
  maxy=getmaxy();
  FieldAdjust();
  if (argc>1) {  
    sprintf(gamepath,"%s",*++argv);        /* read argument */
    parse(gamepath);
    strcpy(fields[FindField("loadstat")].text, "file loaded");
    fields[FindField("loadstat")].color=LGREEN;
  }
  else {
      readdir();
  }
  face(0);
  Update();
  MouseOn(1);
  while(!f) {
    /* Autonomous tasks */
    if (kbhit()) {
      c=getch();
      switch (c) {
      case 'q':
      case 'Q':
	f=1;
	break;
      }
    }
    /* Mouse controlled tasks */
    if (ReadMouse()) {          /* mouse moved or mouse button pressed */
      if (!mouse.button) latch1=0; /* release button latch */
      c=CheckPoint();
      if (hflag) help(c);
      if (c > -1) {      /* mouse points to a field, look for action */
	switch (fields[c].name[0]) {
	case 'q':
	  f=fields[c].mode&1;
	  break;
	case 'D':  /* Directory liste */
	  if (!latch1&&mouse.button) {
	    latch1++;
	    if (strcmp(fields[c].name, "Drup")==0) {
	      if (--fileptr<2) fileptr=2;
              readdir();
	    }
	    if (strcmp(fields[c].name, "Drdown")==0) {
	      ++fileptr;
	      readdir();
	    }
	    if (strcmp(fields[c].name, "Dpup")==0) {
	      if (--dpptr<0) dpptr=0;
	      readdp();
	    }
	    if (strcmp(fields[c].name, "Dpdown")==0) {
	      ++dpptr;
	      readdp();
	    }
	    if (index(fields[c].name, "Dir")==0) {
	      strcpy(fields[FindField("iplane")].text, fields[c].text);
	    }
	    Update();
	  }
          break;
	case 'L':  /* Load file */
	  if (!latch1&&mouse.button) {
	    latch1++;
	    clearall();
	    switch (parse(gamepath)) {
            case 0:
	      StatusLine("");
	      strcpy(fields[FindField("loadstat")].text, "file loaded");
	      fields[FindField("loadstat")].color=LGREEN;
	      face(0);
	      break;
	    case 1:
	      StatusLine("File not found");
	      break;
	    case 2:
	      StatusLine("ERROR when reading file");
	      strcpy(fields[FindField("loadstat")].text, "* file error *");
	      fields[FindField("loadstat")].color=LRED;
	      break;

	    }
      	    drawfield(FindField("loadstat"), 0);
	  }
          break;
	case 'S':  /* Save  file */
	  if (!latch1&&mouse.button) {
	    latch1++;
	    SaveFile(gamepath, 0);
	    StatusLine("File saved");
	  }
	  break;
	case 'Y':	/* sYstem functions */
	  if (!latch1&&mouse.button) {
	    latch1++;
	    if (strcmp(fields[c].name, "Ysysup")==0) {
	      if (--sysptr<0) sysptr=sysmax;
	    }
	    if (strcmp(fields[c].name, "Ysysdown")==0) {
	      if (++sysptr>sysmax) sysptr=0;
	    }
	    if (strcmp(fields[c].name, "Ysysinsert")==0) {
	      sysptr=++sysmax;
	      strcpy(systems[sysptr].name, "New system");
	      for (i=0; i<SYSTEMS; i++) {  /* Find a vacant sysid */
		for (j=0; j<sysmax; j++) {
		  if (systems[j].sysid==i) break;
		}
		if (j==sysmax) {
		  systems[sysptr].sysid=i;
		  break;
		}
	      }
	    }
            if (page&2) {  /* Editing of parms only allowed on systems page */
 	      if (strcmp(fields[c].name, "Ysysname")==0) {
	        GetString(c);
	        strcpy(systems[sysptr].name, fields[c].text);
	      }
	      if (strcmp(fields[c].name, "Ysyslife")==0) {
	        GetString(c);
                sscanf(fields[c].text, "%lg", &systems[sysptr].lifepoints); 
	      }
	      if (strcmp(fields[c].name, "Ysysid")==0) {
	        GetString(c);
	        sscanf(fields[c].text, "%d", &systems[sysptr].sysid);
	      }
	    }
	    if (strcmp(fields[c].name, "Ysysdelete")==0) {
	      StatusLine("DELETE selected system, are you SURE? y/n");
	      if (getch()=='y') {
		while (sysptr<sysmax) {
		  strcpy(systems[sysptr].name, systems[sysptr+1].name);
                  systems[sysptr].lifepoints=systems[sysptr+1].lifepoints;
		  systems[sysptr].sysid=systems[sysptr+1].sysid;
		  for (i=0; i<EFFECTS; i++) {
		    effects[sysptr].level[i]=effects[sysptr+1].level[i];
		    strcpy(effects[sysptr].code[i], effects[sysptr+1].code[i]);
	  	    effects[sysptr].smoke[i]=effects[sysptr+1].smoke[i];
		    effects[sysptr].amount[i]=effects[sysptr+1].amount[i];
		  }
		  for (i=0; i<BOXES; i++) {
		    boxmaps[i][sysptr]=boxmaps[i][sysptr+1];
		  }
		  sysptr++;
		}
		sysptr=0;
		sysmax--;
	      }
	    }
	    Update();
	  }
	  break;
	case 'E':  /* Effects functions */
	  if (!latch1&&mouse.button) {
	    latch1++;
	    for (i=0; i<EFFECTS; i++) {
	      sprintf(msg, "Efflev%d", i);
 	      if (strcmp(fields[c].name, msg)==0) {
                StatusLine("Enter 1-100");
	        GetString1(c);
                sscanf(fields[c].text, "%d", &effects[sysptr].level[i]);
	      }
	      sprintf(msg, "Effcode%d", i);
	      if (strcmp(fields[c].name, msg)==0) {
		StatusLine("Enter DAMAGE, LEAK, BREAK, or BOMB");
		GetString1(c);
		strcpy(effects[sysptr].code[i], fields[c].text);
              }
	      sprintf(msg, "Effcol%d", i);
 	      if (strcmp(fields[c].name, msg)==0) {
		StatusLine("Enter 0-3");
	        GetString1(c);
                sscanf(fields[c].text, "%d", &effects[sysptr].smoke[i]);
	      }
	      sprintf(msg, "Effamount%d", i);
 	      if (strcmp(fields[c].name, msg)==0) {
		StatusLine("Enter 0-3");
	        GetString1(c);
                sscanf(fields[c].text, "%d", &effects[sysptr].amount[i]);
	      }
	    }
            Update();
	  }
          break;
	case 'G':  /* Gunstation functions */
	  if (!latch1&&mouse.button) {
	    latch1++;
	    if (strcmp(fields[c].name, "Gsightx")==0) {
	      GetString(c);
              sscanf(sightx, "%lg", &sightpoint3.x);
	    }
	    if (strcmp(fields[c].name, "Gsighty")==0) {
	      GetString(c);
              sscanf(sighty, "%lg", &sightpoint3.y);
	    }
	    if (strcmp(fields[c].name, "Gsightz")==0) {
	      GetString(c);
	      sscanf(sightz, "%lg", &sightpoint3.z);
	    }
	    if (strcmp(fields[c].name, "Gunstup")==0) {
	      if (--gstptr<0) gstptr=gstmax;
	    }
	    if (strcmp(fields[c].name, "Gunstdown")==0) {
	      if (++gstptr>gstmax) gstptr=0;
	    }
	    if (strcmp(fields[c].name, "Gunstpup")==0) {
	      if (--gstparmptr<0) gstparmptr=22;
	    }
	    if (strcmp(fields[c].name, "Gunstpdown")==0) {
	      if (++gstparmptr>22) gstparmptr=0;
	    }
	    if (strcmp(fields[c].name, "Gunstparmval")==0) {
	      gstparmed(c, 0);
	    }
	    if (strcmp(fields[c].name, "Gunstped1")==0) {
	      gstparmed(c, -1);
	    }
	    if (strcmp(fields[c].name, "Gunstped2")==0) {
	      gstparmed(c, 1);
	    }
	    if (strcmp(fields[c].name, "Gunstaim1")==0) {
	      autoaim(1, 0);
	    }
	    if (strcmp(fields[c].name, "Gunstaim2")==0) {
	      autoaim(1, 1);
	    }
	    if (strcmp(fields[c].name, "Gunstaim3")==0) {
	      autoaim(1, 2);
	    }
	    if (strcmp(fields[c].name, "Gunstdelete")==0) {
	      gstshift(-1);
	    }
	    if (strcmp(fields[c].name, "Gunstcopy")==0) {
	      gstshift(1);
	    }

	    Update();
          }
	  break;
	case 'g':  /* Guns functions */
	  if (!latch1&&mouse.button) {
	    latch1++;
	    if (strcmp(fields[c].name, "gunmaxammo")==0) {
	      GetString(c);
	      sscanf(fields[c].text, "%d", &guns[gstptr].maxammo[gunptr]);
            }
	    if (strcmp(fields[c].name, "gundefammo")==0) {
	      GetString(c);
	      sscanf(fields[c].text, "%d", &guns[gstptr].defammo[gunptr]);
	    }
	    if (strcmp(fields[c].name, "gunup")==0) {
	      if (--gunptr<0) gunptr=GUNS-1;
	    }
	    if (strcmp(fields[c].name, "gundown")==0) {
	      if (++gunptr>GUNS) gunptr=0;
	    }
	    if (strcmp(fields[c].name, "gunaim1")==0) {
	      autoaim(0, 0);
	    }
	    if (strcmp(fields[c].name, "gunaim2")==0) {
	      autoaim(0, 1);
	    }
	    if (strcmp(fields[c].name, "gunaim3")==0) {
	      autoaim(0, 2);
	    }
	    if (strcmp(fields[c].name, "guntracer")==0) {
	      GetString(c);
	      sscanf(fields[c].text, "%d", &guns[gstptr].tracer[gunptr]);
	    }

            Update();
          }
	  break;	
	case 'B':  /* Boxedit functions */
	  if (!latch1&&mouse.button) {
	    latch1++;
	    if (strcmp(fields[c].name, "Boxup")==0) {
	      if (--boxptr<0) boxptr=boxmax;
	    }
	    if (strcmp(fields[c].name, "Boxdown")==0) {
	      if (++boxptr>boxmax) boxptr=0;
	    }
	    if (strcmp(fields[c].name, "Boxname")==0) {
	      GetString(c);
	      strcpy(boxes[boxptr].name, fields[c].text);
	    }
	    if (strcmp(fields[c].name, "Boxxlo")==0) {
	      GetString(c);
	      sscanf(fields[c].text, "%lg", &boxes[boxptr].xlo);
	    }
	    if (strcmp(fields[c].name, "Boxylo")==0) {
	      GetString(c);
	      sscanf(fields[c].text, "%lg", &boxes[boxptr].ylo);
	    }
	    if (strcmp(fields[c].name, "Boxzlo")==0) {
	      GetString(c);
	      sscanf(fields[c].text, "%lg", &boxes[boxptr].zlo);
	    }
	    if (strcmp(fields[c].name, "Boxxhi")==0) {
	      GetString(c);
	      sscanf(fields[c].text, "%lg", &boxes[boxptr].xhi);
	    }
	    if (strcmp(fields[c].name, "Boxyhi")==0) {
	      GetString(c);
	      sscanf(fields[c].text, "%lg", &boxes[boxptr].yhi);
	    }
	    if (strcmp(fields[c].name, "Boxzhi")==0) {
	      GetString(c);
	      sscanf(fields[c].text, "%lg", &boxes[boxptr].zhi);
	    }
	    if (strcmp(fields[c].name, "Boxinsert")==0) {
	      boxptr=++boxmax;
	      strcpy(boxes[boxptr].name, "New box");
	    }
	    if (strcmp(fields[c].name, "Boxdelete")==0) {
	      StatusLine("DELETE selected box, are you SURE? y/n");
	      if (getch()=='y') {
		while (boxptr<boxmax) {
		  strcpy(boxes[boxptr].name, boxes[boxptr+1].name);
                  boxes[boxptr].xlo=boxes[boxptr+1].xlo;
		  boxes[boxptr].ylo=boxes[boxptr+1].ylo;
		  boxes[boxptr].zlo=boxes[boxptr+1].zlo;
		  boxes[boxptr].xhi=boxes[boxptr+1].xhi;
		  boxes[boxptr].yhi=boxes[boxptr+1].yhi;
		  boxes[boxptr].zhi=boxes[boxptr+1].zhi;
		  for (i=0; i<SYSTEMS; i++) {
		    boxmaps[boxptr][i]=boxmaps[boxptr+1][i];
		  }
		  boxptr++;
		}
		boxptr=0;
		boxmax--;
	      }
	    }
	    if (strcmp(fields[c].name, "Boxlocate")==0) {
	      SaveFile(gamepath, 1);
	      StatusLine("Special locator file saved");
            }
	    Update();
	  }
	  break;
	case 'b':  /* Boxmap functions */
	  if (!latch1&&mouse.button) {
	    latch1++;
	    for (i=0; i<BOXES; i++) {
	      sprintf(msg, "bmval%d", i);
	      if (strcmp(fields[c].name, msg)==0) {
	        GetString(c);
		sscanf(fields[c].text, "%d", &boxmaps[i][sysptr]);
	      }
	    }
            Update();
	  }
	  break;
        case 'e': /* Extras functions */
	  if (!latch1&&mouse.button) {
	    latch1++;
            if(strcmp(fields[c].name, "enew")==0) {
	      extraptr=++extramax;
	      extras[extraptr].type=1;
	    }
	    if (extramax>-1) {
	      if (strcmp(fields[c].name, "edel")==0) {
	        StatusLine("DELETE selected extra, are you SURE? y/n");
	        if (getch()=='y') {
		  while (extraptr<extramax) {
                    extras[extraptr].type=extras[extraptr+1].type;
                    extras[extraptr].smoketype=extras[extraptr+1].smoketype;
                    extras[extraptr].smokecolor=extras[extraptr+1].smokecolor;
                    extras[extraptr].x=extras[extraptr+1].x;
                    extras[extraptr].y=extras[extraptr+1].y;
                    extras[extraptr].z=extras[extraptr+1].z;
		    extraptr++;
		  }
		  extras[extraptr].type=0;
		  extras[extraptr].smoketype=0;
		  extras[extraptr].smokecolor=0;
		  extras[extraptr].x=0;
		  extras[extraptr].y=0;
		  extras[extraptr].z=0;
                  extraptr=0;
		  extramax--;
	        }
	      }
	      if(strcmp(fields[c].name, "enumup")==0) {
	        if (--extraptr<0) extraptr=extramax;
	      }
	      if (strcmp(fields[c].name, "enumdn")==0) {
	        if (++extraptr>extramax) extraptr=0;
	      }
	      if (strcmp(fields[c].name, "esmoke")==0) {
	        GetString(c);
		sscanf(fields[c].text, "%d", &extras[extraptr].smoketype);
		if (extras[extraptr].smoketype>4) extras[extraptr].smoketype=4;
	      }
	      if (strcmp(fields[c].name, "ecolor")==0) {
	        GetString(c);
		sscanf(fields[c].text, "%d", &extras[extraptr].smokecolor);
		if (extras[extraptr].smokecolor>5) extras[extraptr].smokecolor=5;
	      }
	      if (strcmp(fields[c].name, "expos")==0) {
	        GetString(c);
		sscanf(fields[c].text, "%lg", &extras[extraptr].x);
	      }
	      if (strcmp(fields[c].name, "eypos")==0) {
	        GetString(c);
		sscanf(fields[c].text, "%lg", &extras[extraptr].y);
	      }
	      if (strcmp(fields[c].name, "ezpos")==0) {
	        GetString(c);
		sscanf(fields[c].text, "%lg", &extras[extraptr].z);
	      }
	      Update();
	    }
	  }
          break;
	case 'P':
	  if ((fields[c].mode&1)&&mouse.button) {
	    switch (fields[c].name[4]) {
	    case '1':
	      j=1;
	      break;
	    case '2':
	      j=2;
	      break;
	    case '3':
	      j=4;
	      break;
	    case '4':
	      j=8;
	      break;
	    case '5':
	      j=16;
              break;
	    case '6':
	      j=32;
	      break;
	    case '7':
	      j=64;
	      break;
	    case '8':
	      j=128;
              break;
	    }
	    if (page!=j) {
	      page=j;
	      Update();
	      face(0);
	      plot(zoom);
	    }
	  }
	  break;
	case 'i':	/* Data input field */
	  if (mouse.button) {
	    switch (fields[c].type) {
	    case TEXT_FIELD:	/* String entry */
	      GetString(c);
	      if (strcmp(fields[c].name, "iplane")==0) readdp();
	      break;
	    }
	    Update();
	  }
	  break;
	case 'V':
	  if ((fields[c].mode&1)&&mouse.button) {
	    axis=fields[c].text[0];
	    Update();
	  }
	  break;
	case 'z':
	  if ((fields[c].mode&1) && mouse.button && !latch1) {
            latch1++;
	    zoom*=2;
            Update();
	  }
          break;
	case 'Z':
	  if ((fields[c].mode&1) && mouse.button && !latch1) {
            latch1++;
	    zoom/=2;
	    Update();
	  }
	  break;
	case 's':
	  if (mouse.button) {
	    while(mouse.button) {
	      if (ReadMouse()) {
		if (page&16) editbox(boxptr, zoom);
		if (page&8) editgun(gstptr, gunptr, zoom);
                if (page&4) editgst(gstptr, zoom);
		if (page&128) editextra(extraptr, zoom);
		Update();
              }
	    }
            Update();
	  }
	  break;
	case 'p':
	  if ((fields[c].mode&1) && mouse.button && !latch1) {
	    latch1++;
	    switch (fields[c].name[3]) {
	    case 'u':
	      pany+=1/zoom;
	      break;
	    case 'l':
	      panx+=1/zoom;
	      break;
	    case 'c':
	      panx=0;
	      pany=0;
	      break;
	    case 'r':
	      panx-=1/zoom;
	      break;
	    case 'd':
	      pany-=1/zoom;
	      break;
	    }
	    Update();
	  }
	  break;
	case 'h':
	  hflag=fields[c].mode&1;
	  if (!hflag) StatusLine(status);
	  break;
	}
      }
    }
    /* Autonomous, field controlled tasks */
    for (i=0; i<FIELDS; i++) {   /* scan all pages */
      switch (fields[i].type) {
      case MONOSTABLE:   /* release any monostables */
      case MICON:
	if (fields[i].mode&1) {
          if (!mouse.button) {
	    fields[i].mode=fields[i].mode & ~1;
	    drawfield(i, 0);
	  }
	}
        break;
      }
    }
  }
  MouseOn(0);
  closegraph();
  return (0);
}

rate()
{
  double bxsum=0, bxmax=0, bxmin=-1, bx;
  double lpsum=0, lpmax=0, lpmin=-1;
  int i, j, g, k;
//  printf("%s\n", ifile);
//  printf("Defensive:\n");
  for (i=0; i<=boxmax; i++) {
    bx=(boxes[i].xhi-boxes[i].xlo)*(boxes[i].yhi-boxes[i].ylo)*(boxes[i].zhi-boxes[i].zlo);
    bxsum+=bx;
    if (bx>bxmax) bxmax=bx;
    if (bxmin<0) bxmin=bx;
    if (bx<bxmin) bxmin=bx;
  }
//  printf("Boxes volume     : Total=%7.1f  Max=%7.1f  Min=%7.1f Avg=%7.1f\n", bxsum, bxmax, bxmin, bxsum/(double)boxmax);
  for (i=0; i<=sysmax; i++) {
    lpsum+=systems[i].lifepoints;
    if (systems[i].lifepoints>lpmax) lpmax=systems[i].lifepoints;
    if (lpmin<0) lpmin=systems[i].lifepoints;                                
    if (systems[i].lifepoints<lpmin) lpmin=systems[i].lifepoints;
  }
//  printf("System lifepoints: Total=%7.1f  Max=%7.1f  Min=%7.1f Avg=%7.1f\n", lpsum, lpmax, lpmin, sysmax?lpsum/(double)sysmax:0);
  lpsum=0;
  lpmax=0;
  lpmin=-1;
  bxsum=0; /* Re-using some box variables */
  for (i=0; i<=sysmax; i++) {
    for (j=0; j<=boxmax; j++) {
      bxsum+=(double)boxmaps[j][i]*(boxes[j].xhi-boxes[i].xlo)*(boxes[j].yhi-boxes[j].ylo)*(boxes[j].zhi-boxes[j].zlo);
    }
    if (bxsum) bx=systems[i].lifepoints*100/bxsum;
    lpsum+=bx;
    if (bx>lpmax) lpmax=bx;
    if (lpmin<0) lpmin=bx;
    if (bx<lpmin) lpmin=bx;
  }
//  printf("System ratings   : Total=%7.1f  Max=%7.1f  Min=%7.1f Avg=%7.1f\n", lpsum, lpmax, lpmin, sysmax?lpsum/(double)sysmax:(double)0);
    i=FindField("rate1");
    sprintf(fields[i].text, "%7.1f", sysmax?lpsum/(double)sysmax:(double)0);
    drawfield(i, 0);
//  printf("Offensive:\n");
  lpmin=0;
  lpsum=0;
  for (i=0, g=0, k=0; i<=gstmax; i++) {
    if (gst[i].type<2) {
      g++;
      j=0;
      if (gst[i].rate!=0) lpmin=(double)gst[i].damage/gst[i].rate;
      while (guns[i].maxammo[j++]) {
	k++;
	lpsum+=lpmin;
      }
    }
  }
//  printf("Gunstations: %d  Total guns: %d\n", g, k);
//  printf("Damage/sec: %7.1f (%d guns)\n", lpsum, k);
    i=FindField("rate2");
    sprintf(fields[i].text, "%7.1f", lpsum);
    drawfield(i, 0);
  return(0);
}      


clearall()   /* Delete all profile data */
{
  int i, j;
    /* Boxes */
  for (i=0; i<BOXES; i++) {
    strcpy(boxes[i].name, "");
    boxes[i].xlo=0;
    boxes[i].ylo=0;
    boxes[i].zlo=0;
    boxes[i].xhi=0;
    boxes[i].yhi=0;
    boxes[i].zhi=0;
  }
  /* Systems */
  for (i=0; i<SYSTEMS; i++) {
    strcpy(systems[i].name, "");
    systems[i].lifepoints=0;
    systems[i].sysid=0;
  }
  /* Boxmaps */
  for (j=0; j<BOXES; j++) {
    for (i=0; i<SYSTEMS; i++) boxmaps[j][i]=0;
  }
  /* Effects */
  for (j=0; j<SYSTEMS; j++) {
    for (i=0; i<EFFECTS; i++) {
      effects[j].level[i]=0;
      strcpy(effects[j].code[i], "");
      effects[j].smoke[i]=0;
      effects[j].amount[i]=0;
    }
  }
  /* Gunstations */
  for (i=0; i<GST; i++) {
    gst[i].type=0;
    gst[i].sysid=0;
    gst[i].trigger=0;
    gst[i].rate=0;
    gst[i].velocity=0;
    gst[i].alive=0;
    gst[i].flash=0;
    gst[i].range=0;
    gst[i].sound=0;
    gst[i].tracer=0;
    strcpy(gst[i].dice, "");
    gst[i].damage=0;
    gst[i].xpos=0;
    gst[i].ypos=0;
    gst[i].zpos=0;
    gst[i].pitch=0;
    gst[i].bank=0;
    gst[i].heading=0;
    gst[i].leftangle=0;
    gst[i].rightangle=0;
    gst[i].upangle=0;
    gst[i].downangle=0;
    gst[i].weight=0;
  }
  /* Guns */
  for (j=0; j<GST; j++) {
    for (i=0; i<GUNS; i++) {
      guns[j].maxammo[i]=0;
      guns[j].xoff[i]=0;
      guns[j].yoff[i]=0;
      guns[j].zoff[i]=0;
      guns[j].pitch[i]=0;
      guns[j].heading[i]=0;
      guns[j].tracer[i]=0;
      guns[j].defammo[i]=0;
    }
  }
  /* Extras */
  for (j=0; j<EXTRAS; j++) {
    extras[i].type=0;
    extras[i].smoketype=0;
    extras[i].smokecolor=0;
    extras[i].x=0;
    extras[i].y=0;
    extras[i].z=0;
  }
  extraptr=0;
  extramax=-1;
  return(0);
}

readdir()
{
  int done, i=0;
  done=findfirst("aircraft\\*", &ffblk, FA_DIREC);
  while (!done&&(i++<fileptr))
  {
     done=findnext(&ffblk);
  }
  strcpy(airplane, ffblk.ff_name);
  if (fileptr==i) --fileptr;
  dpptr=0;
  readdp();
  return(0);
}

readdp()
{
  int done, i=0;
  char s[80];
  sprintf(s, "aircraft\\%s\\*.dp", airplane);
  done=findfirst(s, &ffblk, 0);
  while (!done&&(i++<dpptr))
  {
     done=findnext(&ffblk);
  }
  strcpy(dpfile, ffblk.ff_name);
  if (dpptr>i) --dpptr;
  return(0);
}

gstshift(int m)
{
  int i;
  if (m==-1) {
    StatusLine("DELETE selected gunstation AND associated guns? y/n");
    if (getch()!='y') {
      StatusLine("");
      return(0);
    }
    for (i=gstptr; i<gstmax; i++) {
      gstcopy(i, i+1);
    }
    gstptr=0;
    gstmax--;
  }
  if (m==1) {
    for (i=gstmax; i>=gstptr; i--) {
      gstcopy(i+1, i);
    }
    gstptr++;
    gstmax++;
  }
  return(0);
}

gstcopy(int to, int from)
{
  int i;
  gst[to].type=gst[from].type;
  gst[to].sysid=gst[from].sysid;
  gst[to].trigger=gst[from].trigger;
  gst[to].rate=gst[from].rate;
  gst[to].velocity=gst[from].velocity;
  gst[to].alive=gst[from].alive;
  gst[to].flash=gst[from].flash;
  gst[to].range=gst[from].range;
  gst[to].sound=gst[from].sound;
  gst[to].tracer=gst[from].tracer;
  strcpy(gst[to].dice, gst[from].dice);
  gst[to].damage=gst[from].damage;
  gst[to].xpos=gst[from].xpos;
  gst[to].ypos=gst[from].ypos;
  gst[to].zpos=gst[from].zpos;
  gst[to].pitch=gst[from].pitch;
  gst[to].bank=gst[from].bank;
  gst[to].heading=gst[from].heading;
  gst[to].leftangle=gst[from].leftangle;
  gst[to].rightangle=gst[from].rightangle;
  gst[to].upangle=gst[from].upangle;
  gst[to].downangle=gst[from].downangle;
  gst[to].weight=gst[from].weight;
  for (i=0; i<GUNS; i++) {
    guns[to].maxammo[i]=guns[from].maxammo[i];
    guns[to].xoff[i]=guns[from].xoff[i];
    guns[to].yoff[i]=guns[from].yoff[i];
    guns[to].zoff[i]=guns[from].zoff[i];
    guns[to].pitch[i]=guns[from].pitch[i];
    guns[to].heading[i]=guns[from].heading[i];
    guns[to].tracer[i]=guns[from].tracer[i];
    guns[to].defammo[i]=guns[from].defammo[i];
  }
  return(0);
}

autoaim(int m, int n)	/* Perform autoaim; m=1: Gunstation, m=0: Gun; n=0: Near target, n=1: Far target, n=2 User target */
{
  double targetx, targety, targetz, gunx, guny, pitch, heading;
  switch (m) {
  case 0:
    gunx=gst[gstptr].xpos+guns[gstptr].xoff[gunptr];
    guny=gst[gstptr].ypos+guns[gstptr].yoff[gunptr];
    break;
  case 1:
    gunx=gst[gstptr].xpos;
    guny=gst[gstptr].ypos;
    break;
  }
  switch (n) {
  case 0:
    targetx=sightpoint1.x;
    targety=sightpoint1.y;
    targetz=sightpoint1.z;
    break;
  case 1:
    targetx=sightpoint2.x;
    targety=sightpoint2.y;
    targetz=sightpoint2.z;
    break;
  case 2:
    targetx=sightpoint3.x;
    targety=sightpoint3.y;
    targetz=sightpoint3.z;
    break;
  }
  heading=-atan((gunx-targetx)/(targetz+1))*DEGREES;
  pitch=atan((guny-targety)/(targetz+1))*DEGREES;
  switch (m) {
  case 0:
    guns[gstptr].heading[gunptr]=heading-gst[gstptr].heading;
    guns[gstptr].pitch[gunptr]=pitch-gst[gstptr].pitch;
    break;
  case 1:
    gst[gstptr].heading=heading;
    gst[gstptr].pitch=pitch;
    break;
  }
  return(0);
}


gstparmed(int i, int m)
{
  int j;
  double a;
  switch (gstparmptr) {
  case 0:
    gst[gstptr].type+=m;
    if (gst[gstptr].type<0) gst[gstptr].type=4;
    if (gst[gstptr].type>4) gst[gstptr].type=0;
    break;
  case 1:
    j=sysnum(gst[gstptr].sysid);	/* Find index to current system */
    j+=m; if (j<0) j=sysmax; if (j>sysmax) j=0; /* Add change and check boundaries */
    gst[gstptr].sysid=systems[j].sysid; /* Rread id of new system back */
    break;
  case 2:
    gst[gstptr].trigger+=m;
    if (gst[gstptr].trigger<0) gst[gstptr].trigger=8;
    if (gst[gstptr].trigger>8) gst[gstptr].trigger=0;
    break;
  case 3:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &a);
      if (a) gst[gstptr].rate=1/(a/60);
      else gst[gstptr].rate=0;
    }
    break;
  case 4:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].velocity);
    }
    else gst[gstptr].velocity-=m*50;
    break;
  case 5:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].alive);
    }
    break;
  case 6:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].flash);
    }
    break;
  case 7:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%d", &gst[gstptr].range);
    }
    else gst[gstptr].range-=m*50;
    break;
  case 8:
    gst[gstptr].sound+=m;
    if (gst[gstptr].sound<0) gst[gstptr].sound=7;
    if (gst[gstptr].sound>7) gst[gstptr].sound=0;
    break;
  case 9:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%d", &gst[gstptr].tracer);
    }
    else gst[gstptr].tracer-=m*10;
    break;
  case 10:
    if (!m) GetString(i);
    strcpy(gst[gstptr].dice, fields[i].text);
    break;
  case 11:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%d", &gst[gstptr].damage);
    }
    else gst[gstptr].damage-=m;
    break;
  case 12:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].weight);
    }
    break;
  case 13:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].xpos);
    }
    break;
  case 14:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].ypos);
    }
    break;
  case 15:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].zpos);
    }
    break;
  case 16:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].pitch);
    }
    break;
  case 17:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].bank);
    }
    break;
  case 18:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].heading);
    }
    break;
  case 19:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].leftangle);
    }
    break;
  case 20:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].rightangle);
    }
    break;
  case 21:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].upangle);
    }
    break;
  case 22:
    if (!m) {
      GetString(i);
      sscanf(fields[i].text, "%lg", &gst[gstptr].downangle);
    }
    break;
  }
  return(0);
}

Update()		/* Update screen and various dependencies */
{
  char s[80];
  int i, j, m;
  if (page&1) {		/* Main page */
    sprintf(ifile, "%s.dp", dpfile);
    sprintf(ofile, "%s.dp", dpfile);
    sprintf(gamepath, ".\\aircraft\\%s\\%s", airplane, dpfile);
    drawfield(FindField("idir"), 0);
    fields[i].color=WHITE;
    drawfield(i, 0);
    drawfield(FindField("iplane"), 0);
    drawfield(FindField("idpfile"), 0);
    for (j=fileptr, m=0; j<fileptr+16; j++) {
      if (j>255) break;
      sprintf(s, "Dir%d", m++);
      i=FindField(s);
      drawfield(i, 0);
    }
  }
  if (page&66) {		/* Systems page + boxmap page */
    i=FindField("Ysysno");
    sprintf(fields[i].text, "%d", sysptr);
    drawfield(i, 0);
    i=FindField("Ysysname");
    sprintf(fields[i].text, "%s", systems[sysptr].name);
    drawfield(i, 0);
    i=FindField("Ysyslife");
    sprintf(fields[i].text, "%lg", systems[sysptr].lifepoints);
    drawfield(i, 0);
    i=FindField("Ysysid");
    sprintf(fields[i].text, "%d", systems[sysptr].sysid);
    drawfield(i, 0);
    for (j=0; j<EFFECTS; j++) {
      sprintf(s, "Effno%d", j);
      i=FindField(s);
      sprintf(fields[i].text, "%d", j);
      drawfield(i, 0);
      sprintf(s, "Efflev%d", j);
      i=FindField(s);
      sprintf(fields[i].text, "%d", effects[sysptr].level[j]);
      drawfield(i, 0);
      sprintf(s, "Effcode%d", j);
      i=FindField(s);
      sprintf(fields[i].text, "%s", effects[sysptr].code[j]);
      drawfield(i, 0);
      sprintf(s, "Effcol%d", j);
      i=FindField(s);
      if (strcmp(effects[sysptr].code[j], "LEAK")==0) sprintf(fields[i].text, "%s", effect[effects[sysptr].smoke[j]]);
      else sprintf(fields[i].text, "%d", effects[sysptr].smoke[j]);
      drawfield(i, 0);
      sprintf(s, "Effamount%d", j);
      i=FindField(s);
      if (strcmp(effects[sysptr].code[j], "LEAK")==0) sprintf(fields[i].text, "%s", severity[effects[sysptr].amount[j]]);
      else sprintf(fields[i].text, "%d", effects[sysptr].amount[j]);
      drawfield(i, 0);
    }
  }
  if (page&4) {		/* Gunstations page */
    i=FindField("Gunstnum");
    sprintf(fields[i].text, "%d", gstptr);
    drawfield(i, 0);
    i=FindField("Gunstparmnam");
    j=FindField("Gunstparmval");
    switch (gstparmptr) {
    case 0:
      strcpy(fields[i].text, "Type");
      sprintf(fields[j].text, "%s", guntype[gst[gstptr].type]);
      m=0;
      break;
    case 1:
      strcpy(fields[i].text, "System");
      sprintf(fields[j].text, "%s", systems[sysnum(gst[gstptr].sysid)].name);
      m=0;
      break;
    case 2:
      strcpy(fields[i].text, "Trigger");
      sprintf(fields[j].text, "%s", trigger[gst[gstptr].trigger]);
      m=0;
      break;
    case 3:
      strcpy(fields[i].text, "Firing rate");
      if (gst[gstptr].rate!=0) sprintf(fields[j].text, "%lg", 1/gst[gstptr].rate*60);
      else strcpy(fields[j].text, "-");
      m=1;
      break;
    case 4:
      strcpy(fields[i].text, "Muzzle velocity");
      sprintf(fields[j].text, "%lg", gst[gstptr].velocity);
      m=1;
      break;
    case 5:
      strcpy(fields[i].text, "Time alive");
      sprintf(fields[j].text, "%lg", gst[gstptr].alive);
      m=1;
      break;
    case 6:
      strcpy(fields[i].text, "Flash duration");
      sprintf(fields[j].text, "%lg", gst[gstptr].flash);
      m=1;
      break;
    case 7:
      strcpy(fields[i].text, "Range");
      sprintf(fields[j].text, "%d", gst[gstptr].range);
      m=1;
      break;
    case 8:
      strcpy(fields[i].text, "Sound");
      sprintf(fields[j].text, "%s", gunsound[gst[gstptr].sound]);
      m=0;
      break;
    case 9:
      strcpy(fields[i].text, "Tracer %");
      sprintf(fields[j].text, "%d", gst[gstptr].tracer);
      m=1;
      break;
    case 10:
      strcpy(fields[i].text, "Damage dice");
      sprintf(fields[j].text, "%s", gst[gstptr].dice);
      m=1;
      break;
    case 11:
      strcpy(fields[i].text, "Damage level");
      sprintf(fields[j].text, "%d", gst[gstptr].damage);
      m=1;
      break;
    case 12:
      strcpy(fields[i].text, "Ammo weight");
      sprintf(fields[j].text, "%lg", gst[gstptr].weight);
      m=1;
      break;
    case 13:
      strcpy(fields[i].text, "X pos.");
      sprintf(fields[j].text, "%lg", gst[gstptr].xpos);
      m=1;
      break;
    case 14:
      strcpy(fields[i].text, "Y pos.");
      sprintf(fields[j].text, "%lg", gst[gstptr].ypos);
      m=1;
      break;
    case 15:
      strcpy(fields[i].text, "Z pos.");
      sprintf(fields[j].text, "%lg", gst[gstptr].zpos);
      m=1;
      break;
    case 16:
      strcpy(fields[i].text, "Pitch");
      sprintf(fields[j].text, "%lg", gst[gstptr].pitch);
      m=1;
      break;
    case 17:
      strcpy(fields[i].text, "Bank");
      sprintf(fields[j].text, "%lg", gst[gstptr].bank);
      m=1;
      break;
    case 18:
      strcpy(fields[i].text, "Heading");
      sprintf(fields[j].text, "%lg", gst[gstptr].heading);
      m=1;
      break;
    case 19:
      strcpy(fields[i].text, "Left angle");
      sprintf(fields[j].text, "%lg", gst[gstptr].leftangle);
      m=1;
      break;
    case 20:
      strcpy(fields[i].text, "Right angle");
      sprintf(fields[j].text, "%lg", gst[gstptr].rightangle);
      m=1;
      break;
    case 21:
      strcpy(fields[i].text, "Up angle");
      sprintf(fields[j].text, "%lg", gst[gstptr].upangle);
      m=1;
      break;
    case 22:
      strcpy(fields[i].text, "Down angle");
      sprintf(fields[j].text, "%lg", gst[gstptr].downangle);
      m=1;
      break;
    default:
      gstparmptr=0;
      Update();
    }
    if (m) {
      fields[j].color=WHITE;
      fields[j].mode=BLUE;
    }
    else {
      fields[j].color=LGREEN;
      fields[j].mode=BLACK;
    }
    drawfield(i, 0);
    drawfield(j, 0);
  }
  if (page&8) {		/* Guns page */
    i=FindField("gunstnum");
    sprintf(fields[i].text, "%d", gstptr);
    drawfield(i, 0);
    i=FindField("gunstnam");
    sprintf(fields[i].text, "%s", systems[sysnum(gst[gstptr].sysid)].name);
    drawfield(i, 0);
    i=FindField("gunnum");
    sprintf(fields[i].text, "%d", gunptr);
    drawfield(i, 0);
    i=FindField("gunmaxammo");
    sprintf(fields[i].text, "%d", guns[gstptr].maxammo[gunptr]);
    drawfield(i, 0);
    i=FindField("gundefammo");
    sprintf(fields[i].text, "%d", guns[gstptr].defammo[gunptr]);
    drawfield(i, 0);
    i=FindField("guntracer");
    sprintf(fields[i].text, "%d", guns[gstptr].tracer[gunptr]);
    drawfield(i, 0);
  }
  if (page&16) {	/* Box page */
    sprintf(fields[FindField("Boxnum")].text, "%d", boxptr);
    drawfield(FindField("Boxnum"), 0);
    strcpy(fields[FindField("Boxname")].text, boxes[boxptr].name);
    drawfield(FindField("Boxname"), 0);
    sprintf(fields[FindField("Boxxlo")].text, "%lg", boxes[boxptr].xlo);
    drawfield(FindField("Boxxlo"), 0);
    sprintf(fields[FindField("Boxylo")].text, "%lg", boxes[boxptr].ylo);
    drawfield(FindField("Boxylo"), 0);
    sprintf(fields[FindField("Boxzlo")].text, "%lg", boxes[boxptr].zlo);
    drawfield(FindField("Boxzlo"), 0);
    sprintf(fields[FindField("Boxxhi")].text, "%lg", boxes[boxptr].xhi);
    drawfield(FindField("Boxxhi"), 0);
    sprintf(fields[FindField("Boxyhi")].text, "%lg", boxes[boxptr].yhi);
    drawfield(FindField("Boxyhi"), 0);
    sprintf(fields[FindField("Boxzhi")].text, "%lg", boxes[boxptr].zhi);
    drawfield(FindField("Boxzhi"), 0);
  }
  if (page & 64) {	/* Boxmap page */
    for (j=0; j<BOXES; j++) {
      sprintf(s, "bmval%d", j);
      i=FindField(s);
      sprintf(fields[i].text, "%d", boxmaps[j][sysptr]);
      drawfield(i, 0);
    }
  }
  if (page & 128) {
    i=FindField("enum");
    if (extramax>-1) sprintf(fields[i].text, "%d", extraptr);
    else strcpy(fields[i].text, "none");
    drawfield(i, 0);
    i=FindField("esmoke");
    if (extramax>-1) sprintf(fields[i].text, "%s", smoketype[extras[extraptr].smoketype]);
    else strcpy(fields[i].text, "none");
    drawfield(i, 0);
    i=FindField("ecolor");
    if (extramax>-1) sprintf(fields[i].text, "%s", smokecolor[extras[extraptr].smokecolor]);
    else strcpy(fields[i].text, "none");
    drawfield(i, 0);
    i=FindField("expos");
    if (extramax>-1) sprintf(fields[i].text, "%lg", extras[extraptr].x);
    else strcpy(fields[i].text, "none");
    drawfield(i, 0);
    i=FindField("eypos");
    if (extramax>-1) sprintf(fields[i].text, "%lg", extras[extraptr].y);
    else strcpy(fields[i].text, "none");
    drawfield(i, 0);
    i=FindField("ezpos");
    if (extramax>-1) sprintf(fields[i].text, "%lg", extras[extraptr].z);
    else strcpy(fields[i].text, "none");
    drawfield(i, 0);

  }
  if (page & fields[FindField("screen")].page) {	/* Display */
    plot(zoom);
    sprintf(fields[FindField("dispzoom")].text, "%lgm", 1/zoom);
    drawfield(FindField("dispzoom"), 0);
  }
  return(0);
}

sysnum(int i)		/* Find number of system with id i */
{
  int j;
  for (j=0; j<SYSTEMS; j++) {
    if (systems[j].sysid==i) break;
  }
  return(j);
}

editbox(int i, double z)
{
  #define CATCHZONE 12
  int cx, cy, x1, y1, x2, y2, f, mousex, mousey;
  f=FindField("screen");
  setviewport(fields[f].x+1, fields[f].y+1, fields[f].x+fields[f].width-1, fields[f].y+fields[f].height-1, 1);
  mousex=mouse.x-(fields[f].x+1);
  mousey=mouse.y-(fields[f].y+1);
  lzoom=(double)((fields[f].width-1)/10)*z; /* Zoom 1= 10m across */
  cx=fields[f].width/2+(int)(panx*lzoom);
  cy=fields[f].height/2+(int)(pany*lzoom);
  switch (axis) {       /* locate current position of vertexes */
  case 'Y':
    x1=(int)(boxes[i].xlo*lzoom)+cx;
    x2=(int)(boxes[i].xhi*lzoom)+cx;
    y1=-(int)(boxes[i].zlo*lzoom)+cy;
    y2=-(int)(boxes[i].zhi*lzoom)+cy;
    break;
  case 'X':
    x1=-(int)(boxes[i].zlo*lzoom)+cx;
    x2=-(int)(boxes[i].zhi*lzoom)+cx;
    y1=-(int)(boxes[i].ylo*lzoom)+cy;
    y2=-(int)(boxes[i].yhi*lzoom)+cy;
    break;
  case 'Z':
    x1=(int)(boxes[i].xlo*lzoom)+cx;
    x2=(int)(boxes[i].xhi*lzoom)+cx;
    y1=-(int)(boxes[i].ylo*lzoom)+cy;
    y2=-(int)(boxes[i].yhi*lzoom)+cy;
    break;
  }
  if ((mousex>x1-CATCHZONE)&&(mousex<x1+CATCHZONE)&&(mousey>y1-CATCHZONE)&&(mousey<y1+CATCHZONE)) {
    x1=mousex;
    y1=mousey;
  }
  else {	/* Catch only one vertex at a time */
    if ((mousex>x2-CATCHZONE)&&(mousex<x2+CATCHZONE)&&(mousey>y2-CATCHZONE)&&(mousey<y2+CATCHZONE)) {
      x2=mousex;
      y2=mousey;
    }
  }
  switch (axis) {       /* read new vertexes back */
  case 'Y':
    boxes[i].xlo=(double)(x1-cx)/lzoom;
    boxes[i].xhi=(double)(x2-cx)/lzoom;
    boxes[i].zlo=-(double)(y1-cy)/lzoom;
    boxes[i].zhi=-(double)(y2-cy)/lzoom;
    break;
  case 'X':
    boxes[i].zlo=-(double)(x1-cx)/lzoom;
    boxes[i].zhi=-(double)(x2-cx)/lzoom;
    boxes[i].ylo=-(double)(y1-cy)/lzoom;
    boxes[i].yhi=-(double)(y2-cy)/lzoom;
    break;
  case 'Z':
    boxes[i].xlo=(double)(x1-cx)/lzoom;
    boxes[i].xhi=(double)(x2-cx)/lzoom;
    boxes[i].ylo=-(double)(y1-cy)/lzoom;
    boxes[i].yhi=-(double)(y2-cy)/lzoom;
    break;
  }
  setviewport(0, 0, maxx, maxy, 1);
  return(0);
}

editgst(int i, double z)
{
  int cx, cy, x1, y1, f, mousex, mousey;
  f=FindField("screen");
  setviewport(fields[f].x+1, fields[f].y+1, fields[f].x+fields[f].width-1, fields[f].y+fields[f].height-1, 1);
  mousex=mouse.x-(fields[f].x+1);
  mousey=mouse.y-(fields[f].y+1);
  lzoom=(double)((fields[f].width-1)/10)*z; /* Zoom 1= 10m across */
  cx=fields[f].width/2+(int)(panx*lzoom);
  cy=fields[f].height/2+(int)(pany*lzoom);
  x1=mousex;
  y1=mousey;
  switch (axis) {       /* read new positions back */
  case 'Y':
    gst[i].xpos=(double)(x1-cx)/lzoom;
    gst[i].zpos=-(double)(y1-cy)/lzoom;
    break;
  case 'X':
    gst[i].zpos=-(double)(x1-cx)/lzoom;
    gst[i].ypos=-(double)(y1-cy)/lzoom;
    break;
  case 'Z':
    gst[i].xpos=(double)(x1-cx)/lzoom;
    gst[i].ypos=-(double)(y1-cy)/lzoom;
    break;
  }
  setviewport(0, 0, maxx, maxy, 1);
  return(0);
}

editgun(int i, int j, double z)
{
  int cx, cy, x1, y1, f, mousex, mousey;
  f=FindField("screen");
  setviewport(fields[f].x+1, fields[f].y+1, fields[f].x+fields[f].width-1, fields[f].y+fields[f].height-1, 1);
  mousex=mouse.x-(fields[f].x+1);
  mousey=mouse.y-(fields[f].y+1);
  lzoom=(double)((fields[f].width-1)/10)*z; /* Zoom 1= 10m across */
  cx=fields[f].width/2+(int)(panx*lzoom);
  cy=fields[f].height/2+(int)(pany*lzoom);
  x1=mousex;
  y1=mousey;
  switch (axis) {       /* read new positions back */
  case 'Y':
    guns[i].xoff[j]=(double)(x1-cx)/lzoom-gst[i].xpos;
    guns[i].zoff[j]=-(double)(y1-cy)/lzoom-gst[i].zpos;
    break;
  case 'X':
    guns[i].zoff[j]=-(double)(x1-cx)/lzoom-gst[i].zpos;
    guns[i].yoff[j]=-(double)(y1-cy)/lzoom-gst[i].ypos;
    break;
  case 'Z':
    guns[i].xoff[j]=(double)(x1-cx)/lzoom-gst[i].xpos;
    guns[i].yoff[j]=-(double)(y1-cy)/lzoom-gst[i].ypos;
    break;
  }
  setviewport(0, 0, maxx, maxy, 1);
  return(0);
}

editextra(int i, double z)
{
  int cx, cy, x1, y1, f, mousex, mousey;
  f=FindField("screen");
  setviewport(fields[f].x+1, fields[f].y+1, fields[f].x+fields[f].width-1, fields[f].y+fields[f].height-1, 1);
  mousex=mouse.x-(fields[f].x+1);
  mousey=mouse.y-(fields[f].y+1);
  lzoom=(double)((fields[f].width-1)/10)*z; /* Zoom 1= 10m across */
  cx=fields[f].width/2+(int)(panx*lzoom);
  cy=fields[f].height/2+(int)(pany*lzoom);
  x1=mousex;
  y1=mousey;
  switch (axis) {       /* read new positions back */
  case 'Y':
    extras[i].x=(double)(x1-cx)/lzoom;
    extras[i].z=-(double)(y1-cy)/lzoom;
    break;
  case 'X':
    extras[i].z=-(double)(x1-cx)/lzoom;
    extras[i].y=-(double)(y1-cy)/lzoom;
    break;
  case 'Z':
    extras[i].x=(double)(x1-cx)/lzoom;
    extras[i].y=-(double)(y1-cy)/lzoom;
    break;
  }
  setviewport(0, 0, maxx, maxy, 1);
  return(0);
}



plot(double z)
{
  int f;
  int cx, cy, mx, my, x, y, i;
  f=FindField("screen");
  if (page & fields[f].page) {
    drawfield(f, 0);
    MouseOn(2);
    setviewport(fields[f].x+1, fields[f].y+1, fields[f].x+fields[f].width-1, fields[f].y+fields[f].height-1, 1);
    mx=fields[f].width-1;
    my=fields[f].height-1;
    lzoom=(double)(mx/10)*z; /* Zoom 1= 10m across */
    cx=fields[f].width/2+(int)(panx*lzoom);
    cy=fields[f].height/2+(int)(pany*lzoom);
	/* Draw grid */
    setcolor(BLUE);
    for (x=cx; x<mx; x+=(int)(lzoom/z)) line(x, 0, x, my);
    for (x=cx; x>0; x-=(int)(lzoom/z)) line(x, 0, x, my);
    for (y=cy; y<my; y+=(int)(lzoom/z)) line(0, y, mx, y);
    for (y=cy; y>0; y-=(int)(lzoom/z)) line(0, y, mx, y);
    setcolor(LGRAY);
    for (i=0; i<=boxmax; i++) {
      drawbox(i, lzoom, cx, cy, 1);
    }
    if (page&16) {
      setcolor(WHITE);
      drawbox(boxptr, lzoom, cx, cy, 0);
    }
    if (page&12) {
      for (i=0; i<=gstmax; i++) {
	drawgst(i, lzoom, cx, cy, BROWN);
      }
      drawgst(gstptr, lzoom, cx, cy, YELLOW);
      drawsights(lzoom, cx, cy, LBLUE);
    }
    if (page&8) {
      for (i=0; i<=gstmax; i++) {
	drawguns(i, -1, lzoom, cx, cy, CYAN);
      }
      drawguns(gstptr, gunptr, lzoom, cx, cy, LIGHTCYAN);
    }
    if (page&128) {
      for (i=0; i<=extramax; i++) {
	drawextra(i, lzoom, cx, cy, GREEN);
      }
      drawextra(extraptr, lzoom, cx, cy, LIGHTGREEN);
    }
    setviewport(0, 0, maxx, maxy, 1);
    MouseOn(1);
  }
  return(0);
}

drawbox(int i, double z, int cx, int cy, int m)	/* Draw a box, m=0: With indicated vertexes, m=1: no vertexes */
{
  int x1, y1, x2, y2, z1=4;
  switch (axis) {
  case 'Y':
    x1=(int)(boxes[i].xlo*z)+cx;
    x2=(int)(boxes[i].xhi*z)+cx;
    y1=-(int)(boxes[i].zlo*z)+cy;
    y2=-(int)(boxes[i].zhi*z)+cy;
    break;
  case 'X':
    x1=-(int)(boxes[i].zlo*z)+cx;
    x2=-(int)(boxes[i].zhi*z)+cx;
    y1=-(int)(boxes[i].ylo*z)+cy;
    y2=-(int)(boxes[i].yhi*z)+cy;
    break;
  case 'Z':
    x1=(int)(boxes[i].xlo*z)+cx;
    x2=(int)(boxes[i].xhi*z)+cx;
    y1=-(int)(boxes[i].ylo*z)+cy;
    y2=-(int)(boxes[i].yhi*z)+cy;
    break;
  }
  switch (m) {
  case 0:
    setfillstyle(SOLID_FILL, LGRAY);
    bar(x1-z1,y1-z1,x1+z1,y1+z1);
    bar(x2-z1,y2-z1,x2+z1,y2+z1);
  case 1:
    rectangle(x1,y1,x2,y2);
    break;
  }
  return(0);
}

drawguns(int i, int g, double z, int cx, int cy, int color)	/* Draw guns for gunstation i. If g=-1, draw all, else use g as gunptr */
{
  int j, x1, y1, x2, y2, z1=4;
  for (j=(g>-1?g:0); j<=(g>-1?g:GUNS-1); j++) {
    if(guns[i].maxammo[j]==0) break;
    switch (axis) {
    case 'Y':
      x1=(int)((gst[i].xpos+guns[i].xoff[j])*z)+cx;
      y1=-(int)((gst[i].zpos+guns[i].zoff[j])*z)+cy;
      if (gst[i].range) {
	x2=x1+(int)((double)gst[i].range*sin((gst[i].heading+guns[i].heading[j])*RADIANS)*z);
	y2=-(int)((double)gst[i].range*z)+cy;
	setcolor(color);
	line(x1, y1, x2, y2);
      }
      break;
    case 'X':
      x1=-(int)((gst[i].zpos+guns[i].zoff[j])*z)+cx;
      y1=-(int)((gst[i].ypos+guns[i].yoff[j])*z)+cy;
      if (gst[i].range) {
	y2=y1+(int)((double)gst[i].range*sin((gst[i].pitch+guns[i].pitch[j])*RADIANS)*z);
	x2=-(int)((double)gst[i].range*z)+cy;
	setcolor(color);
	line(x1, y1, x2, y2);
      }
      break;
    case 'Z':
      x1=(int)((gst[i].xpos+guns[i].xoff[j])*z)+cx;
      y1=-(int)((gst[i].ypos+guns[i].yoff[j])*z)+cy;
      break;
    }
    setfillstyle(SOLID_FILL, color);
    bar(x1-z1,y1-z1,x1+z1,y1+z1);
  }
  return(0);
}


drawgst(int i, double z, int cx, int cy, int color)	/* Draw a gunstation */
{
  int x1, y1, x2, y2, z1=4;
  switch (axis) {
  case 'Y':
    x1=(int)(gst[i].xpos*z)+cx;
    y1=-(int)(gst[i].zpos*z)+cy;
    if (gst[i].range) {
      x2=x1+(int)((double)gst[i].range*sin(gst[i].heading*RADIANS)*z);
      y2=-(int)((double)gst[i].range*z)+cy;
      setcolor(color);
      line(x1, y1, x2, y2);
    }
    break;
  case 'X':
    x1=-(int)(gst[i].zpos*z)+cx;
    y1=-(int)(gst[i].ypos*z)+cy;
    if (gst[i].range) {
      y2=y1+(int)((double)gst[i].range*sin(gst[i].pitch*RADIANS)*z);
      x2=-(int)((double)gst[i].range*z)+cy;
      setcolor(color);
      line(x1, y1, x2, y2);
    }
    break;
  case 'Z':
    x1=(int)(gst[i].xpos*z)+cx;
    y1=-(int)(gst[i].ypos*z)+cy;
    break;
  }
  setfillstyle(SOLID_FILL, color);
  bar(x1-z1,y1-z1,x1+z1,y1+z1);
  return(0);
}

drawsights(double z, int cx, int cy, int color)	/* Draw sightpoints */
{
  int x1, y1, x2, y2, x3, y3, z1=3;
  setfillstyle(SOLID_FILL, color);
  switch (axis) {
  case 'Y':
    x1=(int)(sightpoint1.x*z)+cx;
    y1=-(int)(sightpoint1.z*z)+cy;
    x2=(int)(sightpoint2.x*z)+cx;
    y2=-(int)(sightpoint2.z*z)+cy;
    x3=(int)(sightpoint3.x*z)+cx;
    y3=-(int)(sightpoint3.z*z)+cy;
    bar(x1-z1,y1-z1,x1+z1,y1+z1);
    bar(x2-z1,y2-z1,x2+z1,y2+z1);
    bar(x3-z1,y3-z1,x3+z1,y3+z1);
    break;
  case 'X':
    x1=-(int)(sightpoint1.z*z)+cx;
    y1=-(int)(sightpoint1.y*z)+cy;
    x2=-(int)(sightpoint2.z*z)+cx;
    y2=-(int)(sightpoint2.y*z)+cy;
    x3=-(int)(sightpoint3.z*z)+cx;
    y3=-(int)(sightpoint3.y*z)+cy;
    bar(x1-z1,y1-z1,x1+z1,y1+z1);
    bar(x2-z1,y2-z1,x2+z1,y2+z1);
    bar(x3-z1,y3-z1,x3+z1,y3+z1);
    break;
  }
  return(0);
}

drawextra(int i, double z, int cx, int cy, int color)	/* Draw an EXTRA */
{
  int x1, y1, x2, y2, z1=4;
  if (extras[i].type) {
    switch (axis) {
    case 'Y':
      x1=(int)(extras[i].x*z)+cx;
      y1=-(int)(extras[i].z*z)+cy;
      break;
    case 'X':
      x1=-(int)(extras[i].z*z)+cx;
      y1=-(int)(extras[i].y*z)+cy;
      break;
    case 'Z':
      x1=(int)(extras[i].x*z)+cx;
      y1=-(int)(extras[i].y*z)+cy;
      break;
    }
    setfillstyle(SOLID_FILL, color);
    bar(x1-z1,y1-z1,x1+z1,y1+z1);
  }
  return(0);
}


GetString(int c)	/* User inputs string to fields[c].text */
{
  int cc, i=0;
  StatusLine("Enter value");
  fields[c].text[i]='_';
  fields[c].text[i+1]='\0';
  drawfield(c, 0);
  while ((cc=getch())!=13) {
    switch (cc) {
    case 8:	/* Backspace */
      i--;
      break;
    default:
      fields[c].text[i++]=cc;
      break;
    }
    fields[c].text[i]='_';
    fields[c].text[i+1]='\0';
    drawfield(c, 0);
  }
  fields[c].text[i]='\0';
  drawfield(c, 0);
  StatusLine("");
  return(0);
}

GetString1(int c)	/* User inputs string to fields[c].text */
{                       /* no change of message line */
  int cc, i=0;
  fields[c].text[i]='_';
  fields[c].text[i+1]='\0';
  drawfield(c, 0);
  while ((cc=getch())!=13) {
    switch (cc) {
    case 8:	/* Backspace */
      i--;
      break;
    default:
      fields[c].text[i++]=cc;
      break;
    }
    fields[c].text[i]='_';
    fields[c].text[i+1]='\0';
    drawfield(c, 0);
  }
  fields[c].text[i]='\0';
  drawfield(c, 0);
  StatusLine("");
  return(0);
}


GetNum(int c)		/* User inputs number to fields[c].value */
{
  return(c);
}

SaveFile(char *file, int f)		/* Save .dp file;  if f==1 save special box locator version */
{
  FILE *fp;
  int i, j, k;
  char path[255];
  sprintf(path,"%s", file);
  fp=fopen(path, "w");
  /* Boxes */
  fprintf(fp, "[BOXES]\n");
  for (i=0; i<=boxmax; i++) {
    fprintf(fp, "box.%d=%s,%lg,%lg,%lg,%lg,%lg,%lg\n", i, boxes[i].name, boxes[i].xlo, boxes[i].ylo, boxes[i].zlo, boxes[i].xhi, boxes[i].yhi, boxes[i].zhi);
  }
  fprintf(fp, "\n");
  /* Systems */
  fprintf(fp, "[SYSTEMS]\n");
  for (i=0; i<=sysmax; i++) {
    fprintf(fp, "system.%d=%%system_name.%d%%,%lg,%d\n", i, i, systems[i].lifepoints, systems[i].sysid);
  }
  fprintf(fp, "\n");
  /* Boxmaps */
  for (j=0; j<=boxmax; j++) {
    k=0;
    fprintf(fp, "[BOXMAPS.%d]\n; Box = %s\n", j, boxes[j].name);
    for (i=0; i<=sysmax; i++) {
      if (boxmaps[j][i]) {
	fprintf(fp, "boxmap.%d=%d,%d\n", k++, boxmaps[j][i], systems[i].sysid);
      }
    }
    fprintf(fp, "\n");
  }
  /* Effects */
  for (j=0; j<=sysmax; j++) {
    if (strlen(systems[j].name)==0) break;
    else {
      fprintf(fp, "[EFFECTS.%d]\n; System = %s\n", j, systems[j].name);
      for (i=0; i<EFFECTS; i++) {
	if (effects[j].level[i]==0) break;
	else fprintf(fp, "effect.%d=%d,%s,%d,%d\n", i, effects[j].level[i], effects[j].code[i], effects[j].smoke[i], effects[j].amount[i]);
      }
      fprintf(fp, "\n");
    }
  }
  if (f) {  /* Locator mode */
    fprintf(fp, "; **** SPECIAL Box locator gun placements, by DAMEDIT ****\n");
    fprintf(fp, "[GUNSTATIONS]\n");
    fprintf(fp, "gunstation.0=0,-2,1,0.12,500,1,0.1,100,2,40,1d1*1,%lg,%lg,%lg,0,0,0,0,0,0,0,0.01\n", boxes[boxptr].xlo, boxes[boxptr].ylo, boxes[boxptr].zlo);
    fprintf(fp, "\n");
    fprintf(fp, "[guns.0]\n");
    fprintf(fp, "gun.0=400,0,0,0,0,0,0,400\n");
    fprintf(fp, "gun.1=400,0,0,%lg,0,0,0,400\n", boxes[boxptr].zhi-boxes[boxptr].zlo);
    fprintf(fp, "gun.2=400,%lg,0,%lg,0,0,0,400\n", boxes[boxptr].xhi-boxes[boxptr].xlo, boxes[boxptr].zhi-boxes[boxptr].zlo);
    fprintf(fp, "gun.3=400,%lg,0,0,0,0,0,400\n", boxes[boxptr].xhi-boxes[boxptr].xlo);
    fprintf(fp, "gun.4=400,0,%lg,0,0,0,0,400\n", boxes[boxptr].yhi-boxes[boxptr].ylo);
    fprintf(fp, "gun.5=400,0,%lg,%lg,0,0,0,400\n", boxes[boxptr].yhi-boxes[boxptr].ylo, boxes[boxptr].zhi-boxes[boxptr].zlo);
    fprintf(fp, "gun.6=400,%lg,%lg,%lg,0,0,0,400\n", boxes[boxptr].xhi-boxes[boxptr].xlo, boxes[boxptr].yhi-boxes[boxptr].ylo, boxes[boxptr].zhi-boxes[boxptr].zlo);
    fprintf(fp, "gun.7=400,%lg,%lg,0,0,0,0,400\n", boxes[boxptr].xhi-boxes[boxptr].xlo, boxes[boxptr].yhi-boxes[boxptr].ylo);
    fprintf(fp, "\n");
  }
  else {    /* Normal mode */
    /* Gunstations */
    fprintf(fp, "[GUNSTATIONS]\n");
    for (i=0; i<=gstmax; i++) {
      fprintf(fp, "gunstation.%d=%d,%d,%d,%lg,%lg,%lg,%lg,%d,%d,%d,%s*%d,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg\n", i, gst[i].type, gst[i].sysid, gst[i].trigger, gst[i].rate, gst[i].velocity, gst[i].alive, gst[i].flash, gst[i].range, gst[i].sound, gst[i].tracer, gst[i].dice, gst[i].damage, gst[i].xpos, gst[i].ypos, gst[i].zpos, gst[i].pitch, gst[i].bank, gst[i].heading, gst[i].leftangle, gst[i].rightangle, gst[i].upangle, gst[i].downangle, gst[i].weight);
    }
    fprintf(fp, "\n");
    /* Guns */
    for (j=0; j<=gstmax; j++) {
      fprintf(fp, "[guns.%d]\n", j);
      for (i=0; i<GUNS; i++) {
        if(guns[j].maxammo[i]==0) break;
        else fprintf(fp, "gun.%d=%d,%lg,%lg,%lg,%lg,%lg,%d,%d,\n", i, guns[j].maxammo[i], guns[j].xoff[i], guns[j].yoff[i], guns[j].zoff[i], guns[j].pitch[i], guns[j].heading[i], guns[j].tracer[i], guns[j].defammo[i]);
      }
      fprintf(fp, "\n");
    }
  }
  /* Extras */
  for(i=0; i<=extramax; i++) {
    fprintf(fp, "[EXTRA.%d]\n", i);
    fprintf(fp, "extra_type=%d\n", extras[i].type);
    fprintf(fp, "smoke_type=%d\n", extras[i].smoketype);
    fprintf(fp, "smoke_color=%d\n", extras[i].smokecolor);
    fprintf(fp, "smoke_xoffset=%lg\n", extras[i].x);
    fprintf(fp, "smoke_yoffset=%lg\n", extras[i].y);
    fprintf(fp, "smoke_zoffset=%lg\n", extras[i].z);
    fprintf(fp, "\n");
  }
  /* Strings */
  fprintf(fp, "[STRINGS]\n");
  for (i=0; i<SYSTEMS; i++) {
    if (strlen(systems[i].name)==0) break;
    else fprintf(fp, "\"system_name.%d\"=%s\n", i, systems[i].name);
  }
  fclose(fp);
  return(0);
}

parse(char *file)		/* Parse .dp file, return 0 on success */
{
  FILE *fp;
  int c, f=0, sysid;
  double dummy;	/* This is really wierd, but some of the sscanf calls need this to work */
  char path[255], l[255];
  sprintf(path,"%s", file);
  StatusLine("Reading file");
  if (access(path, 6)) {	/* Check for read/write permission */
    StatusLine("File Error");
    f=1;
  }
  else {
    fp=fopen(path, "r");
    while (fgets(l,254,fp) != NULL ) {
      if ((c=readcom(l))>-1) {     /* does the line contain a command ? */
	switch (c) {
	case 0:		/* box */
	  boxptr=GetPtr(l);
	  if (boxmax<boxptr) boxmax=boxptr;
	  split(l);
	  sscanf(l, "%*s %s %lg %lg %lg %lg %lg %lg", boxes[boxptr].name, &boxes[boxptr].xlo, &boxes[boxptr].ylo, &boxes[boxptr].zlo, &boxes[boxptr].xhi, &boxes[boxptr].yhi, &boxes[boxptr].zhi, &dummy);
	  split(boxes[boxptr].name);
	  break;
	case 1:		/* system */
	  sysptr=GetPtr(l);
	  if (sysmax<sysptr) sysmax=sysptr;
	  split(l);
	  sscanf(l, "%*s %*s %lg %d", &systems[sysptr].lifepoints, &systems[sysptr].sysid);
	  break;
	case 2:		/* boxmap header */
	  boxptr=GetPtr(l);
	  break;
	case 3:		/* boxmap line */
	  split(l);
	  sscanf(l, "%*s %*d %d", &sysid);
	  sscanf(l, "%*s %d %*d", &boxmaps[boxptr][sysnum(sysid)]);
	  break;
	case 4:		/* effects header */
	  sysptr=GetPtr(l);
	  break;
	case 5:		/* effects line */
	  effptr=GetPtr(l);
	  split(l);
	  sscanf(l, "%*s %d %s %d %d", &effects[sysptr].level[effptr], effects[sysptr].code[effptr], &effects[sysptr].smoke[effptr], &effects[sysptr].amount[effptr]);
	  break;
	case 6:		/* gunstation */
	  gstptr=GetPtr(l);
	  if (gstmax<gstptr) gstmax=gstptr;
	  split(l);
	  sscanf(l, "%*s %d %d %d %lg %lg %lg %lg %d %d %d %s %d %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg", &gst[gstptr].type, &gst[gstptr].sysid, &gst[gstptr].trigger, &gst[gstptr].rate, &gst[gstptr].velocity, &gst[gstptr].alive, &gst[gstptr].flash, &gst[gstptr].range, &gst[gstptr].sound, &gst[gstptr].tracer, gst[gstptr].dice, &gst[gstptr].damage, &gst[gstptr].xpos, &gst[gstptr].ypos, &gst[gstptr].zpos, &gst[gstptr].pitch, &gst[gstptr].bank, &gst[gstptr].heading, &gst[gstptr].leftangle, &gst[gstptr].rightangle, &gst[gstptr].upangle, &gst[gstptr].downangle, &gst[gstptr].weight);
	  break;
	case 7:		/* guns header */
	  gstptr=GetPtr(l);
	  break;
	case 8:		/* guns line */
	  gunptr=GetPtr(l);
	  split(l);
	  sscanf(l, "%*s %d %lg	%lg %lg	%lg %lg	%d %d",	&guns[gstptr].maxammo[gunptr], &guns[gstptr].xoff[gunptr], &guns[gstptr].yoff[gunptr], &guns[gstptr].zoff[gunptr], &guns[gstptr].pitch[gunptr], &guns[gstptr].heading[gunptr], &guns[gstptr].tracer[gunptr], &guns[gstptr].defammo[gunptr]);
	  break;
	case 9:		/* strings */
	  sysptr=GetPtr(l);
	  split(l);
	  sscanf(l, "%*s %s", systems[sysptr].name);
	  split(systems[sysptr].name);
	  break;
	case 10:	/* Extras header */
	  extraptr=GetPtr(l);
	  if (extramax<extraptr) extramax=extraptr;
	  break;
        case 11:	/* type */
	  split(l);
	  sscanf(l, "%*s %d", &extras[extraptr].type);
          break;
        case 12:	/* smoke_type */
	  split(l);
	  sscanf(l, "%*s %d", &extras[extraptr].smoketype);
          break;
        case 13:	/* smoke_color */
	  split(l);
	  sscanf(l, "%*s %d", &extras[extraptr].smokecolor);
          break;
        case 14:	/* smoke_xoffset */
	  split(l);
	  sscanf(l, "%*s %lg", &extras[extraptr].x);
	  break;
        case 15:	/* smoke_yoffset */
	  split(l);
	  sscanf(l, "%*s %lg", &extras[extraptr].y);
	  break;
        case 16:	/* smoke_zoffset */
	  split(l);
	  sscanf(l, "%*s %lg", &extras[extraptr].z);
	  break;
	}
      }
    }
  }
  fclose(fp);
  /* Set the pointers to 0 */
  boxptr=0; sysptr=0; effptr=0; gstptr=0; gunptr=0;
  rate();
  return(f);
}

split(char *l)	      /* Condition strings for sscanf */
{
  int i, j, f=0;
  char ll[255];
  strcpy(ll, "");
  for (i=0, j=0; i<strlen(l);) {   /* Add '0' between any double commas */
    if (l[i]==',') {
      if (f) {
       ll[j++]='0';
      }
      f++;
    }
    else f=0;
    ll[j++]=l[i++];
  }
  ll[j]='\0';
  strcpy(l, ll);
  strcpy(ll, "");
  for (i=0, j=0; i<strlen(l);) {   /* Get rid of surplus spaces between comma and number */
    if (l[i]==',') {
      ll[j++]=l[i++];
      while (l[i]==' ') {
	i++;
      }
    }
    else ll[j++]=l[i++];
  }
  ll[j]='\0';
  strcpy(l, ll);
  for (i=0, j=0; i<strlen(l);) {   /* Get rid of multiple spaces */
    if (l[i]==' ') {
      if (l[i+1]==' ') {
	while (l[i+1]==' ') {
	  i++;
	}
	i++;
      }
    }
    ll[j++]=l[i++];
  }
  ll[j]='\0';
  strcpy(l, ll);
  for (i=0; i<strlen(l); i++) {     /* Change fill chars to blanks and blanks to + */
    switch (l[i]) {   
    case '=':
    case ',':
    case '+':
    case '*':
      l[i]=' ';
      break;
    case ' ':
      l[i]='+';
      break;
    }
  }
  return(0);
}

GetPtr(char *l)		/* Read number after . in string l */
{
  int n=0, i=0;
  while (l[i++]!='.');
  while ((l[i]>='0')&&(l[i]<='9')) n=(n*10)+(l[i++]-'0');
  return(n);
}

readcom(s)            /* find command and return its number */
char s[];
{
  int v, i=0;
  v= -1;               /* (return -1 if no command) */
  while (strcmp(coms[i], "END_COMS")) {
    if (index(s,coms[i])==0) {   /* command found (in 1. pos of line)*/
      v=i;
      break;
    }
    else i++;
  }
  return(v);
}

reset()
{
  StatusLine(status);
  return(0);
}

index(s,t)  /* look for string t in string s */
char s[], t[];
{
  int i, j, k;
  for (i = 0; s[i] != '\0'; i++) {
    for (j = i, k = 0; t[k] != '\0' && s[j]==t[k]; j++, k++)
      ;
    if (t[k] == '\0')    /* found */
      return(i);
    }
    return(-1);          /* not found */
}


help(int c)    /* Online help function (sometimes the goto feature is not so bad) */
{
  char msg[80];
  static int oldc;
  if (c != oldc) {
    strcpy(msg, "");
    if (strcmp(fields[c].name, "Load")==0) { sprintf(msg, "Load from file: %s", gamepath); goto finish;}
    if (strcmp(fields[c].name, "Save")==0) { sprintf(msg, "Save file as: %s", gamepath); goto finish;}
    if (strcmp(fields[c].name, "idir")==0) { strcpy(msg, "DP file path"); goto finish;}
    if (strcmp(fields[c].name, "quit")==0) { strcpy(msg, "Exit program"); goto finish;}
    if (strcmp(fields[c].name, "title")==0) { strcpy(msg, "This program is SHAREWARE"); goto finish;}
    if (strcmp(fields[c].name, "iplane")==0) { strcpy(msg, "Enter directory name of airplane"); goto finish;}
    if (strcmp(fields[c].name, "idpfile")==0) { strcpy(msg, "Enter filename of dp file"); goto finish;}
    if (index(fields[c].name, "Dr")==0) { strcpy(msg, "Click to scroll directory"); goto finish;}
    if (index(fields[c].name, "Dp")==0) { strcpy(msg, "Click to scroll dp file"); goto finish;}
    if (index(fields[c].name, "Page")==0) { strcpy(msg, "Select page"); goto finish;}
    if (index(fields[c].name, "Ysys")==0) { strcpy(msg, "Systems functions"); goto finish;}
    if (index(fields[c].name, "Eff")==0) { strcpy(msg, "Effects data"); goto finish;}
    if (index(fields[c].name, "Gunstaim")==0) { strcpy(msg, "Aim selected gunstation at target"); goto finish;}
    if (index(fields[c].name, "Gun")==0) { strcpy(msg, "Gunstation functions"); goto finish;}
    if (index(fields[c].name, "gunst")==0) { strcpy(msg, "Select gunstation"); goto finish;}
    if (index(fields[c].name, "gunaim")==0) { strcpy(msg, "Aim selected gun at target"); goto finish;}
    if (index(fields[c].name, "gun")==0) { strcpy(msg, "Gun functions"); goto finish;}
    if (index(fields[c].name, "Box")==0) { strcpy(msg, "Box functions"); goto finish;}
    if (index(fields[c].name, "bm")==0) { strcpy(msg, "Boxmap functions"); goto finish;}
    if (index(fields[c].name, "oom")==1) { strcpy(msg, "Change zoom"); goto finish;}
    if (index(fields[c].name, "pancent")==0) { strcpy(msg, "Reset pan"); goto finish;}
    if (index(fields[c].name, "pan")==0) { strcpy(msg, "Pan in direction of arrow"); goto finish;}
    if (strcmp(fields[c].name, "dispzoom")==0) { strcpy(msg, "Meters pr. division"); goto finish;}
    if (strcmp(fields[c].name, "screen")==0) { strcpy(msg, "Click and drag handle boxe(es) to edit/move selected item"); goto finish;}
    if (index(fields[c].name, "View")==0) { strcpy(msg, "Select axis to view from, Y=top, X=side, Z=front"); goto finish;}

    finish:
    if (strlen(msg)) StatusLine(msg);
    else StatusLine(status);
    oldc=c;
  }
  return(0);
}


FindField(char *n)  /* return vector to first field with name n (-1 if not found)*/
{
  int f=-1, i;
  for (i=0; i<FIELDS; i++) {
    if (strcmp(n, fields[i].name)==0) {
      f=i;
      break;
    }
  }
  return(f);
}

CheckPoint()   /* check field is pointed, react on mouse button */
{
  int i, j, f=-1;
  for (i=0; i<FIELDS; i++) {
    if ((fields[i].page&page)) {
      if ((mouse.x>fields[i].x)&&(mouse.y>fields[i].y)&&(mouse.x<(fields[i].x+fields[i].width))&&(mouse.y<(fields[i].y+fields[i].height))) {
	if (mouse.button&1) {
	  switch (fields[i].type) {
	  case BUTTON:   /* bistable button */
          case ICON:
	    if (!latch1) {   /* ignore if button still pushed */
              latch1++;
	      fields[i].mode=fields[i].mode ^ 1;
	      drawfield(i, 0);
	    }
	    break;
	  case RADIO:  /*  Radio button */
	    if (!fields[i].mode&1) {
              fields[i].mode=fields[i].mode|1;
	      drawfield(i, 0);
	      for (j=0; j<FIELDS; j++) {
		if ((j != i)&&(fields[j].name[0]==fields[i].name[0])&&(fields[j].mode&1)) {
		  fields[j].mode=fields[i].mode & ~1;
		  drawfield(j, 0);
		}
	      }
	    }
	    break;
	  case MONOSTABLE: /* monostable button (release not handled here) */
	  case MICON:
	    fields[i].mode=fields[i].mode|1;
	    drawfield(i, 0);
	    break;
	  }
	}
	f=i;
      }
    }
  }
  return(f);
}

MouseOn(int f)  /* Mouse control: f==0 reset mouse, f==1 show pointer, f==2 hide pointer. */
{
  regs.x.ax=f;
  int86(0x33, &regs, &regs);
  return(1);
}

ReadMouse() /* Read mouse */
{
  regs.x.ax=3;
  int86(0x33, &regs, &regs);
  if ((mouse.x != regs.x.cx)||(mouse.y != regs.x.dx)||(mouse.button != regs.x.bx)) {
    mouse.x=regs.x.cx;
    mouse.y=regs.x.dx;
    mouse.button=regs.x.bx;
    return(1);
  }
  else return(0);
}

void erase(int x, int y, int x1, int y1, int c)  /* clear area to color c */
{
  setfillstyle(SOLID_FILL, c);
  bar(x, y, x1, y1);
}

FieldAdjust()       /* Adjust fields */
{
  int bx, by, i, n;
  bx=maxx/8;
  by=maxy/8;
  for (i=0; i<FIELDS; i++) {
    switch (fields[i].width) {
    case AUTO:
      fields[i].width=(strlen(fields[i].text)+1)*height;
      break;
    case REPEAT:
      fields[i].width=fields[i-1].width;
      break;
    case BLOCK_1:
    case BLOCK_2:
    case BLOCK_3:
    case BLOCK_4:
    case BLOCK_5:
    case BLOCK_6:
    case BLOCK_7:
    case BLOCK_8:
      n=CENTER-fields[i].width;
      fields[i].width=bx*n-height/2;
      break;
    default:
      fields[i].width*=height;
      break;
    }
    switch (fields[i].height) {
    case AUTO:
      fields[i].height=height+height/2;
      break;
    case REPEAT:
      fields[i].height=fields[i-1].height;
      break;
    case BLOCK_1:
    case BLOCK_2:
    case BLOCK_3:
    case BLOCK_4:
    case BLOCK_5:
    case BLOCK_6:
    case BLOCK_7:
    case BLOCK_8:
      n=CENTER-fields[i].height;
      fields[i].height=by*n-height/2;
      break;
    default:
      fields[i].height*=height;
      break;
    }
    switch (fields[i].x) {
    case AUTO:
      fields[i].x=fields[i-1].x+fields[i-1].width+height/2;
      break;
    case REPEAT:
      fields[i].x=fields[i-1].x;
      break;
    case CENTER:
      fields[i].x=maxx/2-fields[i].width/2;
      break;
    case BLOCK_1:
    case BLOCK_2:
    case BLOCK_3:
    case BLOCK_4:
    case BLOCK_5:
    case BLOCK_6:
    case BLOCK_7:
    case BLOCK_8:
      n=BLOCK_1-fields[i].x;
      fields[i].x=bx*n+height/2;
      break;
    case SPACED:
      fields[i].x=fields[i-1].x+fields[i-1].width+height*2;
      break;
    default:
      fields[i].x*=height;
      break;
    }
    switch (fields[i].y) {
    case AUTO:
      fields[i].y=fields[i-1].y+fields[i-1].height+height/2;
      break;
    case REPEAT:
      fields[i].y=fields[i-1].y;
      break;
    case CENTER:
      fields[i].y=maxy/2-fields[i].height/2;
      break;
    case BLOCK_1:
    case BLOCK_2:
    case BLOCK_3:
    case BLOCK_4:
    case BLOCK_5:
    case BLOCK_6:
    case BLOCK_7:
    case BLOCK_8:
      n=BLOCK_1-fields[i].y;
      fields[i].y=by*n+height/2;
      break;
    case SPACED:
      fields[i].y=fields[i-1].y+fields[i-1].height+height*2;
      break;
    default:
      fields[i].y*=height;
      break;
    }
  }
  return(0);
}
   
face(int m)         /* refresh screen */
{
  int i;
  MouseOn(2);
  switch (m) {
  case 0:           /* full redraw */
    erase(0, 0, maxx, maxy, DGRAY);
    for (i=0; i<FIELDS; i++) {
      drawfield(i, 1);
    }
    break;
  }
  MouseOn(1);
  return(0);
}

void drawfield(int i, int m)   /* refresh field i; if m==1 mouse pointer is not affected (for autonomous functions )*/
{
  char s[255];
  if (fields[i].page & page) {
    if (!m) MouseOn(2);
    switch(fields[i].type) {
      case BUTTON:
      case RADIO:
      case MONOSTABLE:
	button(fields[i].text, fields[i].color, fields[i].x, fields[i].y, fields[i].x+fields[i].width, fields[i].y+fields[i].height, fields[i].mode&1);
	break;
      case TEXT_FIELD:
	rwin(fields[i].text, fields[i].color, fields[i].x, fields[i].y, fields[i].x+fields[i].width, fields[i].y+fields[i].height, fields[i].mode);
	break;
      case FREE_TEXT:
        settextjustify( LEFT_TEXT, CENTER_TEXT );
        setcolor(fields[i].color);
        outtextxy( fields[i].x, fields[i].y, fields[i].text );
	break;
      case ICON:
      case MICON:
	button("", fields[i].color, fields[i].x, fields[i].y, fields[i].x+fields[i].width, fields[i].y+fields[i].height, fields[i].mode&1);
	sprintf(s, "c%d%s", fields[i].color+(fields[i].mode&1), fields[i].text);
	draw(s, fields[i].x+fields[i].width/2, fields[i].y+fields[i].height/2, fields[i].height/height, fields[i].mode);
	break;
    }
    if (!m) MouseOn(1);
  }
}

void erasefield(int i, int m)   /* erase field i; if m==1 mouse pointer is not affected (for autonomous functions )*/
{
  if (fields[i].page & page) {
    if (!m) MouseOn(2);
    setfillstyle( SOLID_FILL, DGRAY);
    bar( fields[i].x, fields[i].y, fields[i].x+fields[i].width, fields[i].y+fields[i].height);
    if (!m) MouseOn(1);
  }
}

void changetextstyle(int font, int direction, int charsize)
{
  int ErrorCode;

  graphresult();			/* clear error code		*/
  settextstyle(font, direction, charsize);
  ErrorCode = graphresult();		/* check result 		*/
  if( ErrorCode != grOk ){		/* if error occured		*/
    closegraph();
    printf(" Graphics System Error: %s\n", grapherrormsg( ErrorCode ) );
    exit( 1 );
  }
}

void StatusLine( char *msg )
{
  int l;
  MouseOn(2);
  button("", 0, 0, maxy-(height+4), maxx, maxy, 1);
  if (strlen(msg)) {
    settextjustify( CENTER_TEXT, TOP_TEXT );
    l=strlen(msg)*height+16;
    button(msg, WHITE, maxx/2-l/2, maxy-(height+4), maxx/2+l/2, maxy, 0);
  }
  MouseOn(1);
}

button(char *s, int c, int x1, int y1, int x2, int y2, int m)  /* draw a "button" from x1, y1 to x2, y2; m=0 for raised, m=1 for depressed */
{
  int l;
  settextjustify( CENTER_TEXT, CENTER_TEXT );
  setlinestyle( SOLID_LINE, 0, NORM_WIDTH );
  setfillstyle( SOLID_FILL, DGRAY);
  bar( x1, y1, x2, y2);
  if (m<2) {   /* modes>=2 : invisible border */
    setcolor( m? BLACK  : LGRAY );
    line(x1, y1, x2, y1);                        /*  -   */
    line(x1, y1, x1, y2);                        /* |    */
    setcolor( m? LGRAY : BLACK );
    line(x2, y2, x2, y1);                        /*    | */
    line(x2, y2, x1, y2);                        /*   _  */
  }
  setcolor(c+m);
  outtextxy( x1+((x2-x1)/2), y1+((y2-y1)/2), s );
  return(0);
}

rwin(char *s, int c, int x1, int y1, int x2, int y2, int m)  /* draw depressed text window from x1, y1 to x2, y2; background color==m */
{
  int l;
  settextjustify( CENTER_TEXT, CENTER_TEXT );
  setlinestyle( SOLID_LINE, 0, NORM_WIDTH );
  setfillstyle( SOLID_FILL, m);
  bar( x1, y1, x2, y2);
  setcolor( BLACK );
  line(x1, y1, x2, y1);                        /*  -   */
  line(x1, y1, x1, y2);                        /* |    */
  setcolor( LGRAY );
  line(x2, y2, x2, y1);                        /*    | */
  line(x2, y2, x1, y2);                        /*   _  */
  setcolor(c);
  outtextxy( x1+((x2-x1)/2), y1+((y2-y1)/2), s );
  return(0);
}

draw(s,x,y,n,r)  /* draw control string s at x,y scaled n rotated r */
char *s;
int x, y, n, r;
{
  int a, b, c, f1, f2, p, x1, x2, y1, y2, cc;
  f1=0;     /* digit flag */
  f2=0;     /* sign flag */
  c=0;
  x1=0;
  y1=0;
  x2=0;
  y2=0;
  r/=90;  /* for the time being, we can only rotate in 90 deg. steps */
  r*=90;
  for ( p=0; p<=strlen(s); p++) {
    switch (s[p]) {
    case 'b':
    case 'd':
    case 'f':
    case 'm':
    case '\0':
    case 'c':
      x1=x2;
      y1=y2;
      if (c=='c') {
	setfillstyle(SOLID_FILL, a);
	setcolor(a);
        cc=a;
      }
      else {
	switch (r) {
        case 360:
        case 0:
          x2=a*n+x;
	  y2=b*n+y;
          break;
        case 90:
          x2=-b*n+x;
          y2=a*n+y;
          break;
        case 180:
          x2=-a*n+x;
          y2=-b*n+y;
          break;      
        case 270:
          x2=b*n+x;
	  y2=-a*n+y;
          break;
        }  
      }
      switch (c) {
      case 'd':
	line(x1, y1, x2, y2);
	break;
      case 'b':
	bar(x1, y1, x2, y2);
	break;
      case 'f':
	floodfill(x2, y2, cc);
	break;
      }
      f1=1;
      f2=0;
      c=s[p];
      a=0;
      b=0;
      break;
    case ',':
      f1++;
      f2=0;
      break;
    case '-':
      f2=1;
      break;
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      switch (f1) {
      case 1:
        if (f2)
	  a=a*10-(s[p]-'0');
	else
          a=a*10+(s[p]-'0');
	break;
      case 2:
	if (f2)
	  b=b*10-(s[p]-'0');
	else
          b=b*10+(s[p]-'0');
	break;
      }
      break;
    }
  }
  return(p);
}



