/*

Civ89 version 1.05 is offered under the following license, very similar to the MIT license:



Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above licensing notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/



/*

Replace 'Foo Bar' in the below line '#define EDITNOTES "Edited by Foo Bar"'
with your name(s). If you make any gameplay modifications (such as changing
army revenues from 10% to 20%), please uncomment the line
'#define GAMEPLAYMODIFIED'.

*/

// Insert your name(s) here
#define EDITNOTES "Edited by Foo Bar"
#define EDITNOTES2 ""

// Please uncomment this line if the gameplay is modified
//#define GAMEPLAYMODIFIED



/* Note: Each scenario file has a 16-byte random "code" used to identify it.
The code for the default scenario is {49854, 918, 50773, 19788, 18976, 37979, 34958, 46238}.
This would be important if you intend to build AI for the default scenario only.
*/

/* C Source File
Created 7/13/03; 8:04:03 PM
Original Author: William Jacobs

Civ89, version 1.05
Changes from version 1.04
-Transport units can now only transport the specified combbination of unit types: ground water, air, and high air.  For backwards compatability, bombers can also be transported in earlier scenarios.
-Fixed bug whereby map files made from CivEdit version 1.000 were not loaded always loaded properly
-Fixed bug whereby mined gold was not removed from a playeer when a mining unit was ordered to move or attack
-Fixed bug whereby when moving a miner unit that is miningg would sometimes erroneously change available orders to "Releas".
-Fixed bug whereby when starting a new game, the scenario  name indicated in the game was altered to "0 " followed by the last unit displayed on the starting units screen
-Fixed bug whereby if no maps were found, when the player  returns to the starting units screen, the text is 4x6 instead of 6x8
-Fixed minor bug in game setup navigation
-Fixed bug whereby the "(empty)" text next to the save fille number was not changed to "(full)" when saving from the game settings screen
-Fixed bug whereby rather than displaying "Drop which?" whhen selecting drop from a bomber with multiple bombs, it displayed the city select screen for a nonexistent city, which could lead to a crash
*/

#define COMMENT_PROGRAM_NAME   "Civ89"
#define COMMENT_VERSION_STRING "1.05"
#define COMMENT_VERSION_NUMBER 1,0,5,0

#include <tigcclib.h>

#define UPCHAR 23
#define DOWNCHAR 24
#define _2nd 4096

enum ORDERS {none, buildcity, buildcanal, mineforgold, buildroads, board};
enum TYPES {gro, wat, air, ha, bom, bmr};

struct PLAYER
{
	//struct TRANSPORT *transport;
	struct UNIT *unit;
	struct CITY *city;
	// gpt is gold per turn per city
	// active is whether a unit is available vis-a-vis technology
	unsigned char cities, /*transports,*/ researching, *researched, *active, gpt, rbonus5, pbonus5, bridge;
	// apos is the positions of the activated units
	// showhp is whether the bars are movement points by default
	char *apos, xc, yc, xo, yo, handicap, curcity, showhp;
	// researchp40 is 40 times the portion of the technology researched (out of 200, 250, 300, or 350)
	short researchp40, units, curunit,
	// Range for gold is 0 - 30,000
			gold;
};

// w and h are actually 1 less than the width and height respectively
struct MAP
{
	char name[20];
	struct TILE *tile;
	unsigned char w, h;
};

/*struct KEYS
{
	short up, down, left, right, let[26], diamond;
};*/

struct NUMS
{
	char scen, map, game, autosave, **player, infos, techs;
	short turnnum, maxturn;
	unsigned short code[8], goldoverflow;
};

// 24 bytes
// Range for total number of technologies is 0 - 63
struct TECH
{
	// No longer in draw width than 57, excluding leading pixel
	char id[17];
	unsigned char prereq[3],
	// prod5 must not exceed 19
								prod5:5, sci5:5, gold:7, age:6, bridge:1;
};

// 16 bytes
/*struct TRANSPORT
{
	short transport[8];
};*/

// Limit 300 units per player
struct UNIT
{
	// uindex means unit index (i.e. Warriors, etc.)
	unsigned short uindex:10, hp10:12,
	// tindex means transporter index, attacked is whether the unit attacked
	// this turn
								tindex:9;
	// percent2 means percantage of task completed x 2
	unsigned char x, y, orders:7, attacked:1, percent2, m2;
};

// Map is 50x50, 1 byte
struct TILE
{
	unsigned char water1:1, river1:1, road1:1, mine1:1, water2:1, river2:1, road2:1, mine2:1;
};

// Maximum of 127 unit types, 31 bytes
struct UNITTYPE
{
	// No longer in draw width than 57, excluding leading pixel; id must not be a null string
	char id[17];
	unsigned char turns10,
	// limit: 99
			d:7,
	// limit: 99
			m:7,
	// limit: 99
			a:7,
	// vsha5 means percentage of attack that can be used versus high air / 5
	/* buildmult10 is a multiplier for the building speed x 10 - engineers = 2x
	   and settlers / builders = 1x */
	// tech = prerequisite, br means blast radius
			vsha5:5, vsa5:5, buildmult10:7, assist5:6, canals:1, tech:6, br:4,
	// cau means consumed after use
	// Range for cost10 is 1 - 99
			cost10:7, mines:7,
	// trans means how many units it can transport
	// Range for trans is 0 - 9
			trans:7, /*bribe:1,*/ cau:1,
	// I snuck transgro, etc. into formerly unused bits
	// At present, transbmr is true iff the scenario was saved using CivEdit version 1.03 or earlier
			type:3, transgro:1, transwat:1, transair:1, transha:1, transbmr:1;
};

// 13 bytes
struct OLDCITY
{
	unsigned char x, y, building;
	// percent is the portion of the production that is complete
	float percent;
};

// 5 bytes
struct CITY
{
	unsigned char x, y, building;
	// percent is the portion of the production that is complete x 1000
	unsigned short percent3200;
};

struct SORTS
{
	// csort[0] is the cheapest unit
	// asort[0] is the unit earliest in the alphabet
	unsigned char *csort, *asort;
};

unsigned long rects[100];
unsigned char *sprites;
short let[26];
char turn;
char getaction(short index, struct PLAYER *plyr, struct UNITTYPE *info, struct NUMS *nums, struct TECH *tech, unsigned char *disp, short *center, struct MAP *map, struct SORTS *sorts, char isenemy);

void DrawStrXor(unsigned char x, char y, const char *s)
{
	DrawStr(x, y, s, A_XOR);
}

/*void debugchar(char a)
{
	char inf[30];
	FontSetSys(F_8x10);
	sprintf(inf, "%d", a);
	ClrScr();
	DrawStrNormal(0, 0, inf);
	ngetchx();
}

void debugint(short a)
{
	char inf[30];
	FontSetSys(F_8x10);
	sprintf(inf, "%d", a);
	ClrScr();
	DrawStrNormal(0, 0, inf);
	ngetchx();
}*/

/*void debugint2(short a)
{
	char inf[30];
	FontSetSys(F_6x8);
	sprintf(inf, "%d", a);
	DrawStr(40, 35, inf, A_NORMAL);
}*/

/*void debugfloat(float a)
{
	char inf[30];
	FontSetSys(F_8x10);
	sprintf(inf, "%f", a);
	ClrScr();
	DrawStrNormal(0, 0, inf);
	ngetchx();
}

void debuglong(long int a)
{
	char inf[30];
	FontSetSys(F_8x10);
	sprintf(inf, "%ld", a);
	ClrScr();
	DrawStrNormal(0, 0, inf);
	ngetchx();
}*/

/*void copyunit(struct UNIT *unit1, struct UNIT *unit2)
{
	unit1->uindex = unit2->uindex;
	unit1->hp10 = unit2->hp10;
	unit1->m2 = unit2->m2;
	unit1->tindex = unit2->tindex;
	unit1->x = unit2->x;
	unit1->y = unit2->y;
	unit1->orders = unit2->orders;
	unit1->percent2 = unit2->percent2;
	unit1->attacked = unit2->attacked;
}

void copycity(struct CITY *city1, struct CITY *city2)
{
	city1->x = city2->x;
	city1->y = city2->y;
	city1->building = city2->building;
	city1->percent1000 = city2->percent1000;
}*/

void tryfree(void *ptr)
{
	if (ptr)
	{
		free(ptr);
	}
}

void error(unsigned char a)
{
	char message[34] = "Error reading ";
	switch(a)
	{
		/*case 1:
			strcpy(message, "Grayscale initialization error");
			break;
		Not necessary due to GrayOnThrow function	
		*/
		case 1:
			strcpy(message + 6, "saving game");
			break;
		case 2:
			strcat(message, "map data");
			break;
		case 3:
			strcat(message, "scenario data");
			break;
		case 4:
			strcpy(message + 6, "loading saved game");
			break;
		case 5:
			strcpy(message, "Timer error");
			break;
		default:
			strcpy(message, "Out of memory");
	};
	DlgMessage("Terminal Error", message, BT_NONE, BT_OK);
}

char adj(char x, struct MAP *map)
{
	//char a;
	if (x < 0)
	{
		return(x + map->w + 1);
	}
	else if (x > map->w)
	{
		return(x - (map->w + 1));
	}
	else
	{
		return(x);
	}
	/*a = map->w + 1;
	return((char)((short)(x + a) % (short)a));*/
}

char xy(char x, char y)
{
	return((y << 3) + (y << 1) + y + x);
}

short mapindex(char x, char y, struct MAP *map)
{
	return(((short)y * (short)(map->w + 2 - (map->w % 2)) + (short)adj(x, map)) >> 1);
}

unsigned char getwater(struct MAP *map, short index, char odd)
{
	if (odd)
	{
		return(map->tile[index].water2);
	}
	else
	{
		return(map->tile[index].water1);
	}
}

unsigned char getriver(struct MAP *map, short index, char odd)
{
	if (odd)
	{
		return(map->tile[index].river2);
	}
	else
	{
		return(map->tile[index].river1);
	}
}

unsigned char getroad(struct MAP *map, short index, char odd)
{
	if (odd)
	{
		return(map->tile[index].road2);
	}
	else
	{
		return(map->tile[index].road1);
	}
}

unsigned char getmine(struct MAP *map, short index, char odd)
{
	if (odd)
	{
		return(map->tile[index].mine2);
	}
	else
	{
		return(map->tile[index].mine1);
	}
}

// freadc means perform FREAD and Compare the result to n
// Returns whether there was a problem in reading
char freadc(void *ptr, unsigned short size, unsigned short n, FILE *input)
{
	return(fread(ptr, size, n, input) != n);
}

char fread1(void *ptr, unsigned short n, FILE *input)
{
	return(freadc(ptr, 1, n, input));
}

char loadscen(char fileindex, struct TECH **tech, struct UNITTYPE **info, char *scenname, struct NUMS *nums, struct SORTS *sorts)
{
	FILE *input;
	char fname[11];
	short a;
	short version;
	struct UNITTYPE *tempinfo;
	sprintf(fname, "main\\scen%d", fileindex);
	if (!(input = fopen(fname, "rb")) ||
				fread1(scenname, 21, input) ||
				freadc(&(nums->code), 2, 8, input) ||
				fread1(&(nums->infos), 1, input))
	{
		return(0);
	}
	tryfree(*info);
	*info = (struct UNITTYPE *)malloc_throw(nums->infos * sizeof(struct UNITTYPE));
	a = (nums->infos + 6) << 3;
	tryfree(sprites);
	sprites = (unsigned char *)malloc_throw(a);
	tryfree(sorts->csort);
	sorts->csort = (unsigned char *)malloc_throw(nums->infos);
	tryfree(sorts->asort);
	sorts->asort = (unsigned char *)malloc_throw(nums->infos);
	if (freadc(*info, sizeof(struct UNITTYPE), nums->infos, input) ||
				fread1(sprites, a, input))
	{
		return(0);
	}
	a = nums->infos;
	if (fread1(sorts->csort, a, input) ||
				fread1(sorts->asort, a, input) ||
				fread1(&(nums->techs), 1, input))
	{
		return(0);
	}
	
	//127 is a flag indicating to read the version number.  If a scenario file from an older version of CivEdit is loaded, it should fail because it will exceed the end of the file.
	if (nums->techs == 127)
	{
		//Read the major version number, then the minor version number
		//Refuse to load from a scenario with a major version number greater than 1
		if (fread1(&version, 2, input) || version > 1 || fread1(&version, 2, input) || fread1(&(nums->techs), 1, input))
		{
			return(0);
		}
	}
	else
	{
		version = 0;
	}
	if (version < 1) {
		tempinfo = *info;
		for(a = 0; a < nums->infos; a++) {
			tempinfo->transgro = tempinfo->transwat = tempinfo->transair = tempinfo->transha = tempinfo->transbmr = (tempinfo->trans ? 1 : 0);
			tempinfo++;
		}
	}
	a = nums->techs;
	tryfree(*tech);
	*tech = (struct TECH *)malloc_throw(a * sizeof(struct TECH));
	return(!freadc(*tech, sizeof(struct TECH), a, input) && !fclose(input));
}

short tiles(struct MAP *map)
{
	return(mapindex(map->w, map->h, map) + 1);
}

static inline char aposend(struct PLAYER *curplyr)
{
	char end;
	for(end = 0; curplyr->apos[end] != 127; end++);
	// end is incremented here because there are three additional options: mining, science, and buying
	return(end + 2);
}

void delay(char delaytime)
{
	unsigned long int t;
	t = OSTimerCurVal(6) - delaytime;
	if (GrayCheckRunning())
	{
		pokeIO(0x600005,0b10111);
	}
	else
	{
		idle();
	}
	while (OSTimerCurVal(6) > t);
}

void setfont4x6()
{
	FontSetSys(F_4x6);
}

short grayngetchx()
{
	pokeIO(0x600005,0b10111);
	return(ngetchx());
}

static inline short nograyngetchx()
{
	idle();
	return(ngetchx());
}

short unknownngetchx()
{
	if (GrayCheckRunning())
	{
		return(grayngetchx());
	}
	else
	{
		return(nograyngetchx());
	}
}

// etc means enter to continue
void etc()
{
	setfont4x6();
	DrawStrXor(39, 91, "Press ENTER to continue");
	while (nograyngetchx() != 13);
}

void drawrect(unsigned char x1, char y1, unsigned char x2, char y2)
{
	SCR_RECT rect;
	/*rect.xy.x0 = x1;
	rect.xy.y0 = y1;
	rect.xy.x1 = x2;
	rect.xy.y1 = y2;*/
	rect = (SCR_RECT){{x1, y1, x2, y2}};
	ScrRectFill(&rect, &rect, A_XOR);
}

void drawrect0(char y1, unsigned char x2, char y2)
{
	drawrect(0, y1, x2, y2);
}

void centertext(const char *s, char y)
{
	DrawStrXor(80 - (DrawStrWidth(s, FontGetSys()) >> 1), y, s);
}

void bigmessage(const char *s)
{
	char *a, b[112], *c, y;
	strcpy(c = b, s);
	FontSetSys(F_8x10);
	ClrScr();
	y = 2;
	while ((a = strchr(c, '^')))
	{
		*a = 0;
		centertext(c, y);
		y += 11;
		c = a + 1;
	}
	centertext(c, y);
}

/*void constbigmessage(const char *s)
{
	char a[66];
	strcpy(a, s);
	bigmessage(a);
}*/

// Centers the viewscreen at the specified coordinates
void centerscreen(struct PLAYER *curplyr, struct MAP *map, char x, char y)
{
	curplyr->xc = adj(x - 5, map);
	//plyr[turn].xo = 5;  Irrelevant because xo always = 5
	// Note that this code may require revision if the scrolling system
	// is changed
	/*if (y < 5)
	{
		curplyr->yc = 0;
	}
	else if (y < map->h - 5)
	{
		curplyr->yc = y - 5;
	}
	else
	{
		curplyr->yc = map->h - 10;
	}*/
	curplyr->yo = y - (curplyr->yc = max(min(y - 5, map->h - 10), 0));
}

// Returns 1 upon error, 0 else
char readandcopycities(FILE **input, struct PLAYER *curplyr)
{
	char a;
	struct OLDCITY *oldcity, *oldcity2;
	struct CITY *city;
	city = curplyr->city;
	oldcity2 = oldcity = (struct OLDCITY *)malloc_throw(curplyr->cities * sizeof(struct OLDCITY));
	if (freadc(oldcity, sizeof(struct OLDCITY), curplyr->cities, *input))
	{
		return(1);
	}
	for(a = 0; a < curplyr->cities; a++)
	{
		city->x = oldcity2->x;
		city->y = oldcity2->y;
		city->building = oldcity2->building;
		city->percent3200 = (short)(oldcity2->percent * 3200.0);
		city++;
		oldcity2++;
	}
	tryfree(oldcity);
	return(0);
}

static inline char loadmap(char fileindex, struct MAP *map, struct PLAYER *plyr)
{
	FILE *input;
	char fname[10];
	short a, c;
	unsigned char b;
	struct PLAYER *curplyr;
	sprintf(fname, "main\\map%d", fileindex);
	//ClrScr();
	if (!(input = fopen(fname, "rb")))
	{
		return(0);
	}
	tryfree(map->tile);
	if (freadc(map, sizeof(struct MAP), 1, input))
	{
		return(0);
	}
	
	//If the first byte is 255, this is a special flag indicating that it should read the version number of the map.  Note that it is impossible for a byte of map data to be 255; if the value for a water field is 1, the three corresponding fields must be 0.
	if (fread1(&b, 1, input))
	{
		return(0);
	}
	if (b != 255)
	{
		c = 100;
		if (fseek(input, -1, SEEK_CUR))
		{
			return(0);
		}
	}
	//Read the version number of the map.  This used to be equal to the version number of CivEdit times 100.
	//Saved map files corresponding to this version of Civ89 have version number 103.
	//Now, c / 100 represents the major version number, while c % 100 represents the minor version number.  If the major version number is greater than 1, it refuses to load.  It will not refuse to load files with a major version number of 1 and a minor version number higher than d.  This might allow for future usage of unused bits without forcing backwards incompatability.
	else if (freadc(&c, 2, 1, input))
	{
		return(0);
	}
	
	//Refuse to load maps with a major version number of 2 or greater.
	if (c >= 200)
	{
		return(0);
	}
	
	a = tiles(map);
	map->tile = (struct TILE *)malloc_throw(a * sizeof(struct TILE));
	if (freadc(map->tile, sizeof(struct TILE), a, input))
	{
		return(0);
	}
	curplyr = plyr;
	for(a = 0; a <= 1; a++)
	{
		if (fread1(&(curplyr->cities), 1, input))
		{
			return(0);
		}
		//tryfree(curplyr->city);
		//curplyr->city = (struct CITY *)malloc_throw(curplyr->cities * sizeof(struct CITY));
		
		//If the version number is equal to 100, load old-style maps structures and convert them to new-style map structures
		if (c > 100)
		{
			if (freadc(curplyr->city, sizeof(struct CITY), curplyr->cities, input))
			{
				return(0);
			}
		}
		else if (readandcopycities(&input, curplyr))
		{
			return(0);
		}
		/*sprintf(fname, "%d", curplyr->cities);
		ClrScr();
		DrawStr(0, 50, fname, A_NORMAL);
		ngetchx();*/
		centerscreen(curplyr, map, curplyr->city->x, curplyr->city->y);
		curplyr->xo = 5;
		/*char inf[30];
		ClrScr();
		sprintf(inf, "%d  %d", plyr[a].city[0].x, plyr[a].city[0].y);
		DrawStr(0, 0, inf, A_NORMAL);
		ngetchx();*/
		curplyr++;
	}
	return(!fclose(input));
}

short compare(short index1, short index2, struct PLAYER *curplyr, struct UNITTYPE *info)
{
	short a;
	struct UNIT *unit1, *unit2;
	unit1 = curplyr->unit + index1;
	unit2 = curplyr->unit + index2;
	if ((a = strcmp(info[unit1->uindex].id, info[unit2->uindex].id)))
	{
		return(a);
	}
	else
	{
		if ((a = unit2->hp10 - unit1->hp10))
		{
			return(a);
		}
		else
		{
			if ((a = unit1->orders - unit2->orders))
			{
				return(a);
			}
			else
			{
				/*
				The following code was removed so all units that are visibly identical will appear in the same
				order each time
				if ((a = plyr[turn].unit[index2].m2 - plyr[turn].unit[index1].m2))
				{
					return(a);
				}
				else
				{*/
					return(index1 > index2);
				//}
			}
		}
	}
}

void addtocenter(struct PLAYER *curplyr, struct UNITTYPE *info, short *center, short addindex, char ci)
{
	char d, e, f;
	d = 0;
	e = ci - 1;
	while (d <= e)
	{
		f = (d + e) >> 1;
		if (compare(center[f], addindex, curplyr, info) > 0)
		{
			e = f - 1;
		}
		else
		{
			d = f + 1;
		}
	}
	for(e = min(ci, 23); e >= d; e--)
	{
		center[e + 1] = center[e];
	}
	center[d] = addindex;
}

/*void setdisp(unsigned char *disp, short *center, struct PLAYER *plyr, char turn, struct UNITTYPE *info, struct MAP *map)
{
	short a, b, c;
	// cx is current x, ci is center index, fi is focus index, ym is maximum y
	char cx, cy, ci, fi, index, city;
	unsigned char *disp1, *disp2;
	struct UNIT *unit, *unit2;
	struct PLAYER *curplyr, *newplyr;
	curplyr = plyr + turn;
	ci = center[25] = city = 0;
	center[26] = 30000;
	fi = xy(curplyr->xo, curplyr->yo);
	for(index = 0; index <= 120; index++)
	{
		disp[index] = 253;
		disp[index + 121] = 0;
	}
	for(a = 0; a <= 1; a++)
	{
		newplyr = plyr + a;
		for(b = 0; b < newplyr->cities; b++)
		{
			cx = adj(newplyr->city[b].x - curplyr->xc, map);
			cy = newplyr->city[b].y - curplyr->yc;
			if (cx >= 0 && cx <= 10 && cy >= 0 && cy <= 10)
			{
				index = xy(cx, cy);
				disp[index] = 254;
				disp[index + 121] += a;
				if (index == fi)
				{
					center[25] = (a << 2);
					center[26] = b;
					city = 1;
				}
			}
		}
	//}
	//for(a = 0; a < numplyrs; a++)
	//{
		for(b = 0; b < newplyr->units; b++)
		{
			unit = newplyr->unit + b;
			cx = adj(unit->x - curplyr->xc, map);
			cy = unit->y - curplyr->yc;
			if (cx >= 0 && cx <= 10 && cy >= 0 && cy <= 10)
			{
				disp1 = disp + (index = xy(cx, cy));
				disp2 = disp1 + 121;
				if (*disp1 < 254)
				{
					if (*disp1 != 253)
					{
						*disp2 += 8;
						unit2 = newplyr->unit + *disp1;
						c = unit->hp10 - unit2->hp10;
						if (unit->tindex == 511 && (c > 0 || (!c && unit->uindex < unit2->uindex)))
						{
							*disp1 = b;
						}
					}
					else
					{
						*disp1 = b;
						*disp2 = a;
					}
				}
				else if (*disp1 == 254)
				{
					*disp1 = 255;
				}
				else //if (*disp2 < 8)
				{
					*disp2 += 8;
				}
				if (index == fi)
				{
					if (center[25] < 4)
					{
						center[25] += (a << 2);
					}
					addtocenter(plyr, info, center, b, ci++, a);
				}
			}
		}
	}
	if (ci > 5 - city)
	{
		center[25]++;
	}
	if (ci != 25)
	{
		center[ci] = 30000;
	}
}*/

void clearmoveto(unsigned char x, char y)
{
	MoveTo(x, y);
	SetCurClip(&(SCR_RECT){{0, 0, 159, 99}});
	SetCurAttr(A_NORMAL);
}

void rectoutline(short x1, short y1, short x2, short y2, short attribute)
{
	/*DrawLine(x1, y1, x2, y1, attribute);
	DrawLine(x2, ++y1, x2, y2, attribute);
	DrawLine(x1 + 1, y2, x2 - 1, y2, attribute);
	DrawLine(x1, y1, x1, y2, attribute);*/
	clearmoveto(x1, y1);
	SetCurAttr(attribute);
	LineTo(x2, y1);
	LineTo(x2, y2);
	LineTo(x1, y2);
	LineTo(x1, y1);
	//DrawClipRect(&(WIN_RECT){x1, y1, x2, y2}, &(SCR_RECT){0, 0, 159, 159}, B_NORMAL);
}

void Sprite8or(unsigned char x, char y, unsigned char *sprite, short plane)
{
	Sprite8(x, y, 8, sprite, GrayGetPlane(plane), SPRT_OR);
}

/*void drawsprite(char x, char y, unsigned char index, unsigned char *sprites, short plane, short attribute)
{
	Sprite8((x << 3) + x + 1, (y << 3) + y + 1, 8, sprites + (index << 3), GrayGetPlane(plane), attribute);
}*/

void drawrect100159(unsigned char y1, unsigned char y2)
{
	drawrect(100, y1, 159, y1 + y2);
}

void revrect(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2)
{
	SCR_RECT rect;
	/*rect.xy.x0 = x1;
	rect.xy.y0 = y1;
	rect.xy.x1 = x2;
	rect.xy.y1 = y2;*/
	rect = (SCR_RECT){{x1, y1, x2, y2}};
	ScrRectFill(&rect, &rect, A_REVERSE);
}

void DrawLineXor(unsigned char x1, char y1, unsigned char x2, char y2)
{
	DrawLine(x1, y1, x2, y2, A_XOR);
}

// Bar assumes that F_6x8 is the current font setting
void bar(char y, short a, short b)
{
	char hp[12];
	unsigned char x, x2;
	sprintf(hp, "%d.%d/%d", a / 10, a % 10, b);
	/* This is impossible:  if (strlen(hp) > 9)
	{
		sprintf(hp, "%d/%d", (a + 5) / 10, b);
	}*/
	x = 104 + strlen(hp) * 6;
	DrawStrXor(102, y, hp);
	if (b)
	{
		x2 = (unsigned char)(101.5 + 5.6 * (float)a / (float)b);
	}
	else
	{
		x2 = 157;
	}
	drawrect(101, y, x2, y + 6);
	if (x <= x2 + 3)
	{
		x = x2 + 1;
	}
	/*DrawLineNormal(x, y, 158, y);
	DrawLineNormal(158, y, 158, y + 6);
	y += 6;
	DrawLineNormal(x, y, 158, y);*/
	clearmoveto(x, y);
	LineTo(158, y);
	LineTo(158, y += 6);
	LineTo(x, y);
}

void setdarkplane()
{
	GraySetAMSPlane(DARK_PLANE);
}

void setlightplane()
{
	GraySetAMSPlane(LIGHT_PLANE);
}

void cleartbar()
{
	setdarkplane();
	revrect(100, 0, 159, 99);
	setlightplane();
	revrect(100, 0, 159, 99);
}

static inline void scimining(char *s, char a)
{
	if (a)
	{
		strcpy(s, "Science");
	}
	else
	{
		strcpy(s, "Mining");
	}
}

void setfont6x8()
{
	FontSetSys(F_6x8);
}

void sprintfd(char *s, short a)
{
	sprintf(s, "%d", a);
}

// draws the at-a-glance information about a city
// planes is minplane + 2 * maxplane
void drawcity(struct CITY *city, short *center, char planes)
{
	char a, s[8];
	setfont6x8();
	if (planes)
	{
		for(a = 0; a <= (center[25] < 4); a++)
		{
			GraySetAMSPlane(a);
			Sprite8or(139, 1, sprites + 8 + ((*center != 30000) << 3), a);
			sprintfd(s, center[26] + 1);
			DrawStrXor(147, 1, s);
		}
	}
	if (city->building < 254)
	{
		sprintf(s, "%2d%%", city->percent3200 >> 5);
		for(a = (planes >> 1); a >= planes % 2; a--)
		{
			GraySetAMSPlane(a);
			Sprite8or(105, 11, sprites + ((city->building + 6) << 3), a);
			DrawStrXor(114, 11, s);
			DrawStrXor(135, 11, "done");
		}
	}
	else
	{
		scimining(s, city->building == 254);
		for(a = (planes >> 1); a >= planes % 2; a--)
		{
			GraySetAMSPlane(a);
			DrawStrXor(104, 11, s);
		}
	}
	setdarkplane();
	/*DrawLineNormal(103, 20, 159, 20);
	DrawLineNormal(103, 9, 137, 9);
	DrawLineNormal(103, 9, 103, 20);
	DrawLineNormal(137, 0, 137, 9);*/
	clearmoveto(137, 0);
	LineTo(137, 9);
	LineTo(103, 9);
	LineTo(103, 20);
	LineTo(159, 20);
}

char xcenter(struct PLAYER *curplyr, struct MAP *map)
{
	return(adj(curplyr->xc + curplyr->xo, map));
}

static inline char ycenter(struct PLAYER *curplyr)
{
	return(curplyr->yc + curplyr->yo);
}

void spacedetails(struct PLAYER *curplyr, struct MAP *map, char plane)
{
	short a;
	char b;
	GraySetAMSPlane(plane);
	a = mapindex(b = xcenter(curplyr, map), ycenter(curplyr), map);
	b %= 2;
	if (getwater(map, a, b))
	{
		Sprite8or(101, 0, sprites, plane);
	}
	else
	{
		if (getriver(map, a, b))
		{
			Sprite8or(110, 0, sprites + 32, plane);
		}
		if (getmine(map, a, b))
		{
			Sprite8or(119, 0, sprites + 40, plane);
		}
		if (getroad(map, a, b))
		{
			Sprite8or(128, 0, sprites + 24, plane);
		}
	}
}

// Returns the index of the final unit that has the focus
/*char centerend(short *center)
{
	char end;
	for(end = -1; end <= 23 && center[end + 1] != 30000; end++);
	return(end);
}*/

void invertcity()
{
	char a;
	for(a = 1; a >= 0; a--)
	{
		GraySetAMSPlane(a);
		drawrect(104, 10, 159, 19);
		drawrect(138, 0, 159, 9);
	}
}

void unitstring(char *s, struct UNIT *unit, struct UNITTYPE *curinfo)
{
	char ord[7] = "-BCMRT";
	//sprintf(s, "%7s", curinfo->id);
	strcpy(s, curinfo->id);
	if (unit->orders)
	{
		if (strlen(s) < 7)
		{
			strcat(s, "      ");
		}
		sprintf(s + 7, "(%c)", ord[unit->orders]);
	}
}

void DrawStrXor100(char y, const char *s)
{
	DrawStrXor(100, y, s);
}

void drawbox(struct PLAYER *curplyr, struct UNITTYPE *info, short *center, char selected, char start, char center25p2, char showhp, char yi)
{
	char a, y, s[17];
	short b, c;
	struct UNITTYPE *curinfo;
	struct UNIT *unit;
	unit = curplyr->unit + center[selected];
	curinfo = info + unit->uindex;
	y = yi + (a = ((2 - center25p2) << 3)) * (selected - start) - 1;
	setlightplane();
	if (!center25p2)
	{
		if (showhp)
		{
			b = unit->hp10;
			c = curinfo->d * 3;
		}
		else
		{
			b = unit->m2 * 5;
			c = curinfo->m;
		}
		bar(y + 9, b, c);
		unitstring(s, unit, curinfo);
		DrawStrXor100(y + 1, s);
	}
	drawrect100159(y, a);
	setdarkplane();
	drawrect100159(y, a);
	setfont4x6();
	DrawStrXor100(94, curinfo->id);
}

void DrawCharXor(unsigned char x, char y, char a)
{
	DrawChar(x, y, a, A_XOR);
}

void moreturns(struct PLAYER *curplyr, short *center, struct UNITTYPE *info, float bonus)
{
	char s[14];
	short a;
	struct CITY *city;
	city = curplyr->city + center[26];
	if (city->building < 254)
	{
		a = (short)ceil((float)(3200 - city->percent3200) / (float)((int)(bonus / (float)info[city->building].turns10) + 1));
		sprintf(s, "%d more turn", a);
		if (a != 1)
		{
			strcat(s, "s");
		}
	}
	else
	{
		*s = 0;
	}
	setfont4x6();
	setdarkplane();
	DrawStrXor100(94, s);
}

// selected = 122 means the city is highlighted; selected = 127 means nothing is highlighted
/* prevselected: There are 4 flags.
	126 = Draw everything
	125 = Draw everything except city, do not invert city
	124 = Draw everything except city, invert city
	123 = Draw nothing, do not invert a second selection
	(0 - 24, 122) = Draw nothing, invert prevselected */
char tbar(char start, /*char end,*/ char selected, char prevselected, char up, short *center, struct PLAYER *plyr, struct UNITTYPE *info, struct MAP *map, float bonus, char disptext)
{
	char a, b, c, d, y, yi, end, center25p2, s[17];
	unsigned char x;
	short e, f;
	struct PLAYER *curplyr;
	struct UNIT *unit;
	struct UNITTYPE *curinfo;
	center25p2 = center[25] % 2;
	curplyr = plyr + (center[25] >> 2);
	for(end = -1; end <= 23 && center[end + 1] != 30000; end++);
	d = plyr[turn].showhp;
	setfont6x8();
	setdarkplane();
	if (!disptext)
	{
		if (prevselected != 125 && center[26] != 30000 && (selected == 122 || (prevselected >= 122 && !(prevselected % 2))))
		{
			b = (prevselected > 124);
			drawcity(curplyr->city + center[26], center, (selected != 122 && b) + (b << 1));
		}
		if (prevselected == 126)
		{
			spacedetails(plyr + turn, map, DARK_PLANE);
		}
	}
	else if (prevselected == 126)
	{
		if (disptext == 1)
		{
			DrawStrXor100(1, "Board");
		}
		else
		{
			DrawStrXor100(1, "Drop");
		}
		DrawStrXor100(9, "which?");
	}
	c = (center[26] == 30000 && !disptext);
	yi = (y = 22 - (c << 3)) + (up << 3);
	/*setdarkplane();
	DrawChar(2, 91, selected + 'a', A_REPLACE);*/
	if (prevselected > 123)
	{
		if (start == 127)
		{
			b = 0;
		}
		else
		{
			b = start;
		}
		if (center25p2)
		{
			c += (8 - up);
			/*if (up)
			{
				c--;
			}*/
			/*for(a = 0; a <= c + 1; a++)
			{
				if (a + b > end || a + b >= 25 || center[a + b] == 30000)
				{
					break;
				}
			}
			if (a > c + 1)*/
			if (b + c < end)
			{
				//setdarkplane();
				DrawCharXor(100, 86, DOWNCHAR);
				if (!up)
				{
					sprintf(s, "%d units total", end + 1);
					//setdarkplane();
					setfont4x6();
					DrawStrXor(110, 87, s);
					setfont6x8();
				}
				c--;
			}
		}
		else
		{
			c += 3;
		}
		if (up)
		{
			//setdarkplane();
			DrawCharXor(100, y, UPCHAR);
			y += 8;
		}
		//end = centerend(center);
		for(a = 0; a <= c; a++)
		{
			//if (center[b] == 30000 || b >= 25)
			if (b > end)
			{
				break;
			}
			unit = curplyr->unit + center[b];
			curinfo = info + unit->uindex;
			unitstring(s, unit, curinfo);
			setdarkplane();
			DrawStrXor100(y, s);
			if (d)
			{
				e = unit->hp10;
				f = curinfo->d * 3;
			}
			else
			{
				e = unit->m2 * 5;
				f = curinfo->m;
			}
			if (center25p2)
			{
				setlightplane();
				if (f)
				{
					x = (unsigned char)(101.5 + 5.7 * (float)e / (float)f);
				}
				else
				{
					x = 158;
				}
				drawrect(101, y, x, y + 6);
			}
			else
			{
				//setdarkplane();
				bar(y += 8, e, f);
				/*
				Now this is covered later
				if (a + start == selected && selected != 127)
				{
					GraySetAMSPlane(LIGHT_PLANE);
					bar(y, e, f);
					DrawStr(100, y - 8, s, A_NORMAL);
				}*/
			}
			y += 8;
			b++;
		}
		/*if (a > c)
		{
			DrawChar(110, 90, c + '0', A_NORMAL);
			DrawChar(130, 90, center[25] + '0', A_NORMAL);
		}*/
		/*if (down)
		{
			GraySetAMSPlane(DARK_PLANE);
			DrawChar(100, y, 24, A_NORMAL);
		}*/
	}
	else if (prevselected < 122)
	{
		drawbox(curplyr, info, center, prevselected, start, center25p2, d, yi);
	}
	if (selected < 122)
	{
		setfont6x8();
		drawbox(curplyr, info, center, selected, start, center25p2, d, yi);
	}
	if ((((prevselected != 123) ^ (selected != 122)) || prevselected == 122 || selected == 127) && center[26] != 30000)
	{
		moreturns(curplyr, center, info, bonus);
	}
	if ((prevselected != 125 && (selected == 122 || prevselected == 122)) || prevselected == 124)
	{
		invertcity();
	}
	return(end);
}

void citystring(struct UNITTYPE *info, short cost, char position, char y)
{
	char s[17];
	if (position >= 0)
	{
		strcpy(s, info->id);
		s[10] = 0;
	}
	else if (position == -3)
	{
		sprintf(s, "Buy (%dg)", cost);
	}
	else
	{
		scimining(s, position == -1);
	}
	DrawStrXor100(y, s);
}

void drawcitybox(struct PLAYER *curplyr, struct UNITTYPE *info, short cost, float bonus, char selected, char start, char yi)
{
	char a, b, y, s[17];
	y = yi + ((selected - start) << 3) - 1;
	setdarkplane();
	drawrect100159(y, 8);
	setlightplane();
	drawrect100159(y, 8);
	//revrect(100, 94, 159, 99);
	if ((selected -= 3) < 0)
	{
		b = 0;
	}
	else
	{
		b = curplyr->apos[selected];
	}
	citystring(info += b, cost, selected, y + 1);
	setdarkplane();
	setfont4x6();
	if (selected >= 0)
	{
		//b = curplyr->apos[selected];
		strcpy(s, info->id);
		//a = (char)ceil((float)info->turns10 / bonus);
		// The following line is designed to avert rounding errors
		a = (char)ceil(3200.0 / (float)((int)(bonus / (float)info->turns10) + 1));
		b = ((a <= 9) << 2);
		if (DrawStrWidth(s, F_4x6) <= 44 + b)
		{
			strcat(s, " ");
		}
		else
		{
			while (DrawStrWidth(s, F_4x6) > 46 + b)
			{
				s[strlen(s) - 1] = 0;
			}
		}
		sprintf(s + strlen(s), "(%d)", a);
	}
	else if (selected == -3)
	{
		sprintf(s, "Buy (%d gold)", cost);
	}
	else
	{
		scimining(s, selected == -1);
	}
	DrawStrXor100(94, s);
}

// draws the toolbar for selecting what a city will produce
// This function has been moved to the getunit function
/* prevselected: There are 4 flags.
	126 = Draw everything
	125 = Draw everything except city, do not invert city
	124 = Draw everything except city, invert city
	[123 = Draw nothing, do not invert a second selection - impossible in this context]
	(0 - 99, [122 - impossible in this context]) = Draw nothing, invert prevselected */
/*void citytbar(char start, char end, char selected, char prevselected, char up, float bonus, short *center, struct PLAYER *curplyr, struct UNITTYPE *info, unsigned char *sprites, short cost, struct MAP *map)
{
	char a, b, c, g, y, yi;
	if (prevselected >= 122 && !(prevselected % 2))
	{
		a = (prevselected == 126);
		drawcity(curplyr->city + center[26], center, sprites, a, a);
		if (a)
		{
			spacedetails(curplyr, map, sprites, DARK_PLANE);
		}
		else if (prevselected == 124)
		{
			invertcity();
		}
	}
	yi = y = 22 + (up << 3);
	setfont6x8();
	if (prevselected > 123)
	{
		end -= 3;
		//if (start == 127)
		//{
		//	g = 0;
		//}
		//else
		//{
			//g = start - 3;
		//}
		if ((g = start - 3) > -1)
		{
			b = curplyr->apos[g];
		}
		else
		{
			b = 0;
		}
		setdarkplane();
		c = 8;
		if (up)
		{
			DrawCharXor(100, 22, UPCHAR);
			c = 7;
		}
		if (c + g < end)
		{
			DrawCharXor(100, 86, DOWNCHAR);
			c--;
		}
		for(a = 0; a <= c; a++)
		{
			if (g > end)
			{
				break;
			}
			setdarkplane();
			citystring(info + b, cost, g, y);
			
			//Now this is performed later
			//if (a + start == selected)
			//{
			//	GraySetAMSPlane(LIGHT_PLANE);
			//	DrawStr(100, y, s, A_NORMAL);
			//}
			y += 8;
			if (++g >= 0)
			{
				b = curplyr->apos[g];
			}
		}
	}
	else
	{
		drawcitybox(curplyr, info, cost, bonus, prevselected, start, yi);
		setfont6x8();
	}
	drawcitybox(curplyr, info, cost, bonus, selected, start, yi);
}*/

static inline void spaceoutline(char x, char y, short attribute)
{
	x *= 9;
	y *= 9;
	rectoutline(x, y, x + 9, y + 9, attribute);
}

void draw(unsigned char *disp, short *center, struct UNITTYPE *info, struct MAP *map, struct PLAYER *plyr)
{
	short a, b, c;
	// cx is current x, ci is center index, fi is focus index
	char d, f[3], g, h, x, y, xc, yc, index;
	unsigned char e, *disp1, *disp2;
	struct UNIT *unit, *unit2;
	struct PLAYER *curplyr, *newplyr;
	curplyr = plyr + turn;
	e = center[25] = 0;
	center[26] = 30000;
	d = xy(xc = curplyr->xo, yc = curplyr->yo);
	disp1 = disp;
	disp2 = disp + 121;
	for(index = 0; index <= 120; index++)
	{
		*(disp1++) = 253;
		*(disp2++) = 0;
	}
	newplyr = plyr;
	for(a = 0; a <= 1; a++)
	{
		for(b = 0; b < newplyr->cities; b++)
		{
			x = adj(newplyr->city[b].x - curplyr->xc, map);
			y = newplyr->city[b].y - curplyr->yc;
			if (x >= 0 && x <= 10 && y >= 0 && y <= 10)
			{
				disp[index = xy(x, y)] = 254;
				disp[index + 121] = a;
				if (index == d)
				{
					center[25] = (a << 2);
					center[26] = b;
				}
			}
		}
		for(b = 0; b < newplyr->units; b++)
		{
			unit = newplyr->unit + b;
			x = adj(unit->x - curplyr->xc, map);
			y = unit->y - curplyr->yc;
			if (x >= 0 && x <= 10 && y >= 0 && y <= 10)
			{
				disp2 = (disp1 = disp + (index = xy(x, y))) + 121;
				if (*disp1 == 254)
				{
					*disp1 = 255;
				}
				else if (*disp1 == 253)
				{
					*disp1 = b;
					*disp2 = a;
				}
				else if (*disp1 == 255)
				{
					*disp2 += 8;
				}
				else
				{
					*disp2 += 8;
					unit2 = newplyr->unit + *disp1;
					c = unit->hp10 - unit2->hp10;
					if (unit->tindex == 511 && (c > 0 || (!c && unit->uindex < unit2->uindex)))
					{
						*disp1 = b;
					}
				}
				if (index == d)
				{
					if (center[25] < 4 && a)
					{
						center[25] += 4;
					}
					addtocenter(plyr + a, info, center, b, e++);
				}
			}
		}
		newplyr++;
	}
	if (e > 4 + (center[26] == 30000))
	{
		center[25]++;
	}
	if (e != 25)
	{
		center[e] = 30000;
	}
	
	setdarkplane();
	ClrScr();
	setlightplane();
	ClrScr();
	/*for(a = 0; a <= 99; a += 9)
	{
		DrawLine(a, 0, a, 99, A_NORMAL);
		DrawLine(0, a, 99, a, A_NORMAL);
	}*/
	//e = 0;
	for(a = 0; a <= 54; a += 27)
	{
		Sprite32(a, 0, 100, rects, GrayGetPlane(LIGHT_PLANE), SPRT_OR);
	}
	Sprite32(72, 0, 100, rects, GrayGetPlane(LIGHT_PLANE), SPRT_OR);
	drawrect(9 * xc + 1, 9 * yc + 1, 9 * xc + 8, 9 * yc + 8);
	spaceoutline(xc, yc, A_REVERSE);
	//disp[xy(plyr[turn].xo, plyr[turn].yo) + 121] >= 8
	/*for(a = 0; a <= 1; a++)
	{
		GraySetAMSPlane(a);*/
		/*DrawLine(b + 3, d, b + 6, d, A_REVERSE);
		DrawLine(b + 3, d + 9, b + 6, d + 9, A_REVERSE);
		DrawLine(b, d + 3, b, d + 6, A_REVERSE);
		DrawLine(b + 9, d + 3, b + 9, d + 6, A_REVERSE);*/
		/*DrawLine(b, d, b + 9, d, A_REVERSE);
		DrawLine(b, d + 9, b + 9, d + 9, A_REVERSE);
		DrawLine(b, d, b, d + 9, A_REVERSE);
		DrawLine(b + 9, d, b + 9, d + 9, A_REVERSE);*/
	//}
	for(y = a = 0; y <= 10; y++)
	{
		for(x = 0; x <= 10; x++)
		{
			if ((h = ((e = disp[a]) == 253)))
			{
				d = 0;
				b = curplyr->xc + x;
				c = mapindex(b, curplyr->yc + y, map);
				if (getwater(map, c, b = adj(b, map) % 2))
				{
					f[d++] = 0;
				}
				else
				{
					if (getroad(map, c, b))
					{
						f[d++] = 3;
					}
					if (getriver(map, c, b))
					{
						f[d++] = 4;
					}
					if (getmine(map, c, b))
					{
						f[d++] = 5;
					}
				}
				b = c = 1;
			}
			else
			{
				if (disp[a + 121] >= 8)
				{
					/*if (plyr[turn].xo == x && plyr[turn].yo == y)
					{
						e = 1;
					}
					else
					{*/
						setdarkplane();
						spaceoutline(x, y, A_NORMAL);
						setlightplane();
						spaceoutline(x, y, A_NORMAL);
					//}
				}
				b = !(disp[a + 121] % 8);
				if (e == 254)
				{
					*f = 1;
				}
				else if (e == 255)
				{
					*f = 2;
				}
				else
				{
					*f = plyr[!b].unit[e].uindex + 6;
				}
				d = (xc == x && yc == y);
				c = (b || d);
				b = (b && d);
				d = 1;
			}
			for(e = b; e <= c; e++)
			{
				GraySetAMSPlane(e);
				for(g = 0; g < d; g++)
				{
					//drawsprite(x, y, f[g], sprites, e);
					// SPRT_XOR is 0, SPRT_OR is 1
					Sprite8(9 * x + 1, 9 * y + 1, 8, sprites + (f[g] << 3), GrayGetPlane(e), h);
				}
			}
			a++;
		}
	}
	/*b = (curplyr->xo << 3) + curplyr->xo;
	d = (curplyr->yo << 3) + curplyr->yo;*/
	/*if (!e)
	{
		GraySetAMSPlane(DARK_PLANE);
		rectoutline(b, d, b + 9, d + 9, A_REVERSE);
	}
	else
	{*/
		xc *= 9;
		yc *= 9;
		for(a = 0; a <= 1; a++)
		{
			GraySetAMSPlane(a);
			for(e = 0; e <= 9; e += 9)
			{
				DrawLine(xc + e, yc + 2, xc + e, yc + 7, A_REVERSE);
				DrawLine(xc + 2, yc + e, xc + 7, yc + e, A_REVERSE);
			}
			/*DrawLine(b, d + 2, b, d + 7, A_REVERSE);
			DrawLine(b + 9, d + 2, b + 9, d + 7, A_REVERSE);
			DrawLine(b + 2, d, b + 7, d, A_REVERSE);
			DrawLine(b + 2, d + 9, b + 7, d + 9, A_REVERSE);*/
		}
	//}
}

// Returns the number of city-turns required to complete research
/*short rcost40(char a)
{
	//return(40 * (200 + 50 * a));
	return(8000 + 2000 * a);
}*/

short cityincome(unsigned char cities, unsigned char gpt)
{
	char a, b;
	short total;
	total = gpt * min(cities, b = 10);
	for(a = 20; a < cities; a += 5)
	{
		total -= b * min(cities - a, 5);
		b += 10;
	}
	/*if (cities > a)
	{
		total -= b * (cities - a);
	}*/
	return(total);
}

unsigned char mines(struct UNIT *unit, struct UNITTYPE *info, struct MAP *map)
{
	short a;
	a = ((short)info[unit->uindex].mines * (short)unit->m2 / (short)info[unit->uindex].m);
	if (getmine(map, mapindex(unit->x, unit->y, map), unit->x % 2))
	{
		return(a);
	}
	else
	{
		return(a >> 1);
	}
}

// updategold updates gold and goldoverflow such that gold + goldoverflow remains the same,
// goldoverflow >= 0, and goldoverflow = 0 or gold = 30000.
void updategold(short *gold, struct NUMS *nums)
{
	if (*gold <= 30000)
	{
		if ((long int)*gold + (long int)nums->goldoverflow <= 30000)
		{
			*gold += nums->goldoverflow;
			nums->goldoverflow = 0;
		}
		else
		{
			nums->goldoverflow += 30000 - *gold;
		}
	}
	else
	{
		nums->goldoverflow += *gold - 30000;
		*gold = 30000;
	}
}

short incminingunits(struct PLAYER *curplyr, struct UNITTYPE *info, struct MAP *map)
{
	short a, b;
	struct UNIT *unit;
	unit = curplyr->unit;
	for(a = b = 0; a < curplyr->units; a++)
	{
		if (unit->orders == mineforgold)
		{
			b += mines(unit, info, map);
		}
		unit++;
	}
	return(b);
}

// This function is to ensure that movement points are consumed (if necessary) in the proper order, that
// is, units with higher unit indeces first
void updateminers(struct PLAYER *curplyr, struct UNITTYPE *info, struct MAP *map, struct NUMS *nums)
{
	short b;
	struct UNIT *unit;
	if (!nums->goldoverflow)
	{
		b = curplyr->gold - incminingunits(curplyr, info, map);
		for(unit = curplyr->unit; /*This is always true: a < curplyr->units &&*/ b < 0; unit++)
		{
			//unit = curplyr->unit + a;
			if (unit->orders == mineforgold)
			{
				b += mines(unit, info, map);
				unit->m2 = 0;
			}
		}
	}
	else
	{
		updategold(&(curplyr->gold), nums);
	}
}

// Sets *numunits so that (*numunits)[x] is the number of units for which uindex = x
void constructnumunits(struct PLAYER *curplyr, short **numunits, struct NUMS *nums)
{
	short a, *b;
	b = *numunits = (short *)malloc(nums->infos << 1);
	for(a = 0; a < nums->infos; a++)
	{
		*(b++) = 0;
	}
	for(a = 0; a < curplyr->units; a++)
	{
		(*numunits)[curplyr->unit[a].uindex]++;
	}
}

short incminingcities(struct PLAYER *curplyr, struct MAP *map)
{
	char a;
	short total;
	struct CITY *city;
	total = 0;
	city = curplyr->city;
	for(a = 0; a < curplyr->cities; a++)
	{
		if (city->building == 255)
		{
			if (getmine(map, mapindex(city->x, city->y, map), city->x % 2))
			{
				total += 16;
			}
			else
			{
				total += 8;
			}
		}
		city++;
	}
	return(total);
}

short income(struct PLAYER *curplyr, struct UNITTYPE *info, struct NUMS *nums, struct MAP *map, struct SORTS *sorts, short **numunits)
{
	// uo (unit overflow) is the number of units that must be payed
	short a, c, total, uo;
	char b;
	total = cityincome(curplyr->cities, curplyr->gpt);
	if ((uo = curplyr->units - (25 + curplyr->cities * 5)) > 0)
	{
		constructnumunits(curplyr, numunits, nums);
		for(a = 0; uo; a++)
		{
			b = sorts->csort[a];
			c = min((*numunits)[b], uo);
			/*if (uo > (c = (*numunits)[b]))
			{*/
				uo -= c;
				total -= c * info[b].cost10;
			/*}
			else
			{
				total -= uo * info[b].cost10;
				uo = 0;
			}*/
		}
	}
	else
	{
		*numunits = NULL;
	}
	return(total + incminingcities(curplyr, map));
}

void highlight(char a)
{
	char b;
	b = a / 16;
	a %= 16;
	switch(b)
	{
		case 0:
			switch(a)
			{
				case 0:
					drawrect0(0, 63, 12);
					break;
				case 1:
					drawrect(68, 0, 103, 12);
					break;
				case 2:
					drawrect0(22, 79, 56);
					drawrect0(14, 51, 21);
					break;
				case 3:
					drawrect(81, 22, 159, 56);
					drawrect(96, 14, 159, 21);
					break;
				case 4:
					drawrect0(58, 79, 99);
					break;
				default:
					drawrect(81, 58, 159, 99);
			};
			break;
		case 1:
			switch(a)
			{
				case 2:
					drawrect(68, 73, 92, 81);
					break;
				case 3:
					drawrect(53, 82, 107, 90);
					break;
				case 1:
					drawrect(55, 63, 73, 71);
					break;
				case 4:
					drawrect(20, 91, 140, 99);
					break;
				default:
					drawrect(67, 54, 73, 62);
			};
			break;
		case 2:
			drawrect0(9 * a + 1, 159, 9 * a + 9);
			break;
		//case 4:
		default:
			if (a--)
			{
				drawrect(1, 9 * a, 7, 9 * a + 8);
			}
			else
			{
				drawrect(68, 90, 92, 98);
			}
			break;
	};
}

char keyletter(short k)
{
	char a;
	short *b;
	if (isalpha(k))
	{
		return(tolower(k));
	}
	else
	{
		b = let;
		for(a = 0; a <= 25; a++)
		{
			if (k == *(b++))
			{
				return(a + 'a');
			}
		}
		return(0);
	}
}

/*void commazeroes(char *s, short a)
{
	if (a < 100)
	{
		if (a >= 10)
		{
			strcat(s, "0");
		}
		else
		{
			strcat(s, "00");
		}
	}
	sprintfd(s + strlen(s), a);
}*/

// comma assumes 0 <= a < 256000000
static inline void comma(char *s, long int a)
{
	/*if (a < 0)
	{
		// \xAD is the negative sign
		strcat(s, "\xAD");
		a = -a;
	}*/
	s += strlen(s);
	if (a <= 99999)
	{
		sprintfd(s, (short)a);
	}
	else
	{
		if (a <= 999999)
		{
			sprintf(s, "%d,", (short)(a / 1000));
		}
		else
		{
			sprintf(s, "%d,", (short)(a / 1000000));
			a %= 100000;
			//commazeroes(s, a / 1000);
			sprintf(s + strlen(s), "%03d", (short)(a / 1000));
		}
		//commazeroes(s, a % 1000);
		sprintf(s + strlen(s), "%03d", (short)(a % 1000));
	}
}

/*void youvs(char *s, long int val1, long int val2)
{
	comma(s, val1);
	strcat(s, " (You) vs ");
	comma(s, val2);
}*/

/*void centerescreen(char *s, char y, unsigned char a)
{
	DrawStrXor((a - DrawStrWidth(s, F_4x6)) >> 1, y, s);
}*/

// Draws the x-position of string s, centered on the left half of the empire screen
void centerescreenl(const char *s, char y)
{
	//centerescreen(s, y, 79);
	DrawStrXor((79 - DrawStrWidth(s, F_4x6)) >> 1, y, s);
}

// Draws string s, centered on the right half of the empire screen
void centerescreenr(const char *s, char y)
{
	//centerescreen(s, y, 241);
	DrawStrXor((241 - DrawStrWidth(s, F_4x6)) >> 1, y, s);
}

// pi means power index
void calculatepi(long int *pi, struct PLAYER *plyr, struct UNITTYPE *info)
{
	short c;
	char a, k;
	struct UNIT *unit;
	for(k = 0; k <= 1; k++)
	{
		*pi = 0;
		for(c = 0; c < plyr->units; c++)
		{
			unit = plyr->unit + c;
			if ((a = info[unit->uindex].a))
			{
				*pi += unit->hp10 + 15 * a;
			}
		}
		pi++;
		plyr++;
	}
}

// Returns the 1 if position is changed, 0 else
char nextcity(struct PLAYER *curplyr, struct MAP *map)
{
	struct CITY *city;
	if (!curplyr->cities)
	{
		return(0);
	}
	curplyr->curcity++;
	/*if (*curcity + 1 != curplyr->cities)
	{
		(*curcity)++;
	}
	else
	{
		*curcity = 0;
	}*/
	city = curplyr->city + (curplyr->curcity %= curplyr->cities);
	centerscreen(curplyr, map, city->x, city->y);
	return(1);
}

// Returns the index of the unit in center if changed, -1 else
char nextunit(unsigned char *disp, short *center, struct MAP *map, struct PLAYER *plyr, struct UNITTYPE *info)
{
	char a;
	short b, d;
	struct PLAYER *curplyr;
	struct UNIT *unit;
	curplyr = plyr + turn;
	d = b = (curplyr->curunit + 1) % curplyr->units;
	/*if (b == curplyr->units)
	{
		b = 0;
	}
	d = b;*/
	do
	{
		unit = curplyr->unit + b;
		if (unit->m2)
		{
			if (unit->x != xcenter(curplyr, map) || unit->y != ycenter(curplyr))
			{
				centerscreen(curplyr, map, unit->x, unit->y);
				//setdisp(disp, center, plyr, turn, info, map);
				draw(disp, center, info, map, plyr);
			}
			curplyr->curunit = b;
			for(a = 0; *(center++) != b; a++);
			return(a);
		}
		/*if (++b == curplyr->units)
		{
			b = 0;
		}*/
		b = (b + 1) % curplyr->units;
	}
	while(b != d);
	return(-1);
}

char dirkeys2(short k)
{
	if (k == KEY_LEFT)
	{
		return(1);
	}
	else if (k == KEY_UP)
	{
		return(2);
	}
	else if (k == KEY_RIGHT)
	{
		return(3);
	}
	else if (k == KEY_DOWN)
	{
		return(4);
	}
	else
	{
		return(0);
	}
}

static inline char getassist5(struct PLAYER *curplyr, struct UNITTYPE *info, char x, char y)
{
	short a;
	char b;
	struct UNIT *unit;
	b = 0;
	for(a = 0; a < curplyr->units; a++)
	{
		unit = curplyr->unit + a;
		if (unit->x == x && unit->y == y)
		{
			/*if (info[unit->uindex].assist5 > b)
			{
				b = info[unit->uindex].assist5;
			}*/
			b = max((short)info[unit->uindex].assist5, b);
		}
	}
	return(b);
}

float getbonus(struct PLAYER *curplyr, struct UNITTYPE *info, struct MAP *map)
{
	return((20.0 + (float)curplyr->pbonus5) * (1600.0 + 80.0 * (float)getassist5(curplyr, info, xcenter(curplyr, map), ycenter(curplyr))));
}

// Clears the toolbar below the position of the city display
void clearbelowcity()
{
	setdarkplane();
	revrect(100, 21, 159, 99);
	setlightplane();
	revrect(100, 21, 159, 99);
}

char getposition(unsigned char *disp, short *center, struct PLAYER *plyr, struct UNITTYPE *info, struct MAP *map, char drawmap)
{
	short k;
	// ox means old x
	// pressedmode indicates whether the user just pressed MODE
	char b, x, y, ox, oy, redraw, redrawtbar, pressedmode;
	struct PLAYER *curplyr;
	if (!GrayCheckRunning())
	{
		GrayOnThrow();
	}
	curplyr = plyr + turn;
	redraw = redrawtbar = 1;
	pressedmode = 0;
	do
	{
		if (redraw)
		{
			if (!drawmap)
			{
				//setdisp(disp, center, plyr, turn, info, map);
				draw(disp, center, info, map, plyr);
			}
			/*else
			{
				setdisp(disp, center, plyr, turn, info, map);
			}*/
		}
		if (redrawtbar)
		{
			if ((pressedmode = (pressedmode && center[26] != 30000)))
			{
				clearbelowcity();
			}
			else if (drawmap == 1)
			{
				cleartbar();
			}
			if (drawmap != -1)
			{
				tbar(127, 127, 126 - pressedmode, 0, center, plyr, info, map, getbonus(plyr + (center[25] >> 2), info, map), 0);
			}
			drawmap = redrawtbar = pressedmode = 0;
		}
		ox = x = xcenter(curplyr, map);
		oy = y = ycenter(curplyr);
		k = grayngetchx();
		if ((b = dirkeys2(k)) == 2)
		{
			y--;
		}
		else if (b == 4)
		{
			y++;
		}
		else if (b == 1)
		{
			x--;
		}
		else if (b)
		{
			x++;
		}
		//else if (k == KEY_UP + _2nd)
		else if ((b = dirkeys2(k - _2nd)) == 2)
		{
			y -= 11;
		}
		else if (b == 4)
		{
			y += 11;
		}
		else if (b == 1)
		{
			x -= 11;
		}
		else if (b)
		{
			x += 11;
		}
		else if (k == KEY_DIAMOND + KEY_UP)
		{
			y = 0;
		}
		else if (k == KEY_DIAMOND + KEY_DOWN)
		{
			y = map->h;
		}
		else if (k == 266)
		{
			// Mode
			curplyr->showhp = !curplyr->showhp;
			redrawtbar = pressedmode = drawmap = 1;
		}
		else if ((b = keyletter(k)) == 'n' && curplyr->units)
		{
			if ((b = nextunit(disp, center, map, plyr, info)) > -1)
			{
				return(b);
			}
		}
		/*else if (k == 13 && center[0] == 30000 && center[26] == 30000)
		{
			k = 0;
		}
		This is covered in the getunit function
		*/
		else if (b == 'c')
		{
			if (nextcity(curplyr, map))
			{
				return(29);
			}
		}
		else if (b == 'h' || k == 268)
		{
			return(33);
		}
		y = min(max(y, 0), map->h);
		if ((redraw = (x != ox || y != oy)))
		{
			centerscreen(curplyr, map, x, y);
			redrawtbar = 1;
		}
	}
	while ((k != 13 || (*center == 30000 && center[26] == 30000)) && k != 264);
	if (k == 264)
	{
		return(26);
	}
	else
	{
		return(28);
	}
}

void delunit(struct PLAYER *curplyr, short index)
{
	short a;
	struct UNIT *unit;
	unit = curplyr->unit + index;
	//char c, x, y;
	/*if ((b = (plyr[turn].unit[index].orders == 1)))
	{
		x = plyr[turn].unit[index].x;
		y = plyr[turn].unit[index].y;
	}*/
	for(a = index; a + 1 < curplyr->units; a++)
	{
		memcpy(unit, unit + 1, sizeof(struct UNIT));
		unit++;
	}
	//curplyr->unit = (struct UNIT *)realloc_throw(curplyr->unit, --curplyr->units * sizeof(struct UNIT));
	curplyr->units--;
	unit = curplyr->unit;
	for(a = 0; a < curplyr->units; a++)
	{
		if (unit->tindex > index && unit->tindex != 511)
		{
			unit->tindex--;
		}
		else if (unit->tindex == index)
		{
			unit->tindex = 511;
			unit->orders = none;
		}
		unit++;
	}
	if (a)
	{
		if (curplyr->curunit > index)
		{
			curplyr->curunit--;
		}
		else if (curplyr->curunit == index)
		{
			/*if (curplyr->curunit + 1 != curplyr->units)
			{
				curplyr->curunit++;
			}
			else
			{
				curplyr->curunit = 0;
			}*/
			curplyr->curunit++;
			curplyr->curunit %= curplyr->units;
		}
	}
}

void delcity(struct PLAYER *curplyr, char index)
{
	short a;
	struct CITY *city;
	city = curplyr->city;
	for(a = index; a + 1 < curplyr->cities; a++)
	{
		memcpy(city + a, city + a + 1, sizeof(struct CITY));
	}
	//city = (struct CITY *)realloc_throw(city, --curplyr->cities * sizeof(struct CITY));
	curplyr->cities--;
}

char gameover(struct PLAYER *plyr)
{
	return((!plyr->units && !plyr->cities) || (!plyr[1].units && !plyr[1].cities));
}

/* This entire function has been moved into the attackspace function.  Also, delsingunit has been renamed
		delunit.
void delunit(struct PLAYER *curplyr, struct UNITTYPE *info, short index)
{
	short a;*/
	/*if (plyr[turn].unit[index].tindex != 127)
	{
		for(a = plyr[turn].unit[index].tindex; a + 1 < plyr[turn].transports; a++)
		{
			for(b = 0; b <= 7 && plyr[turn].transport[a].transport[b + 1] != 30000; b++)
			{
				plyr[turn].transport[a].transport[b] = plyr[turn].transport[a].transport[b + 1];
			}
			if (b <= 7)
			{
				plyr[turn].transport[a].transport[b] = 30000;
			}
		}
		plyr[turn].transport = (struct TRANSPORT *)realloc_throw(plyr[turn].transport, --plyr[turn].transports * sizeof(struct TRANSPORT));
	}*/
	/*if (curplyr->unit[index].tindex == 30000 && info[curplyr->unit[index].uindex].trans)
	{
		for(a = 0; a < curplyr->units; a++)
		{
			if (curplyr->unit[a].tindex == index)
			{
				delunit(curplyr, a);
				a--;
			}
		}
	}
	delunit(curplyr, index);
}*/

char is124(char a)
{
	return(a == 1 || a == 2 || a == 4);
}

// returns twice the total number of turns required to produce the structure
static inline char construction(struct UNIT *unit)
{
	switch(unit->orders)
	{
		case 1:
			return(3);
		case 2:
			return(20);
		default:
			return(6);
	};
}

// Returns the amount of progress in construction besed on unit's statistics
short construction2(struct UNIT *unit, struct UNITTYPE *info)
{
	// Note that this code will require revision if conditions are changed so that it is possible to
	// advance production less than 0.25% even with full movement points
	// 20 = 200 * 2 / (2 * 10)
	return((short)((20.0 * (float)unit->m2 * (float)info[unit->uindex].buildmult10) / ((float)construction(unit) * (float)info[unit->uindex].m) + 0.5));
}

// The build function advances the building of a particular structure
// Returns 1 to redraw the screen, 2 if city construction was delayed due to the city limit, 0 else
char build(struct PLAYER *curplyr, struct UNIT *unit, struct UNITTYPE *info, struct MAP *map, char recentaction)
{
	// Note that b must be an integer because it may exceed 255
	short a, b, c;
	char d;
	struct CITY *city;
	struct UNIT *unit2;
	struct TILE *tile;
	b = c = construction2(unit, info);
	unit2 = curplyr->unit;
	for(a = 0; a < curplyr->units; a++)
	{
		if (unit2->x == unit->x && unit2->y == unit->y && unit2->orders == unit->orders && unit2->percent2)
		{
			b += unit2->percent2;
			break;
		}
		unit2++;
	}
	// Cheat: Use this line to make construction instantaneous
	//b = 200;
	if (b < 200 || (unit->orders == buildcity && curplyr->cities == 50))
	{
		b = min(b, 200);
		unit2 = curplyr->unit;
		for(a = 0; a < curplyr->units; a++)
		{
			if (unit2->x == unit->x && unit2->y == unit->y && unit2->orders == unit->orders)
			{
				unit2->percent2 = b;
			}
			unit2++;
		}
		if (b != 200)
		{
			return(0);
		}
		else
		{
			return(2);
		}
		//return(2 - ((b < 200) << 1));
	}
	else
	{
		d = unit->orders;
		//a = mapindex(unit->x, unit->y, map);
		tile = map->tile + mapindex(unit->x, unit->y, map);
		switch(d)
		{
			case 1:
				// City
				//curplyr->city = (struct CITY *)realloc_throw(curplyr->city, ++curplyr->cities * sizeof(struct CITY));
				city = curplyr->city + curplyr->cities++;
				city->x = unit->x;
				city->y = unit->y;
				city->building = 255;
				//city->assist5 = 0;
				city->percent3200 = 0;
				break;
			case 2:
				// Canal
				if (unit->x % 2)
				{
					tile->river2 = 1;
				}
				else
				{
					tile->river1 = 1;
				}
				break;
			default:
				// Roads
				if (unit->x % 2)
				{
					tile->road2 = 1;
				}
				else
				{
					tile->road1 = 1;
				}
		};
		
		// Cheat: Use this line and comment the succeeding block to preserve unit movement points after
		// construction
		//unit->orders = 0;
		
		
		unit2 = curplyr->unit;
		for(a = 0; a < curplyr->units; a++)
		{
			if (unit2->x == unit->x && unit2->y == unit->y)
			{
				if (unit2->orders == d)
				{
					unit2->percent2 = 0;
					if (recentaction && unit != unit2)
					{
						unit2->m2 =
						// This line is necessary in order for the pre function to identify units used for construction
								unit2->orders = none;
					}
				}
			}
			unit2++;
		}
		if (recentaction)
		{
			//unit->m2 = (unsigned char)((float)info[unit->uindex].m * 2.0 * (1.0 - (float)(200 - b + c) / (float)c));
			unit->m2 -= (unsigned char)ceil((float)info[unit->uindex].m * (float)(200 - b + c) * (float)construction(unit) / (20.0 * (float)info[unit->uindex].buildmult10));
			unit->orders = 0;
		}
		
		
		return(d == 1);
	}
}

char techavailable(struct PLAYER *curplyr, struct TECH *tech, char index)
{
	char b;
	unsigned char c;
	//a = sorts->tsort[index];
	if (curplyr->researched[index])
	{
		return(0);
	}
	else
	{
		for(b = 0; b <= 2; b++)
		{
			c = tech[index].prereq[b];
			if (c == 255)
			{
				return(1);
			}
			else if (!curplyr->researched[c])
			{
				return(0);
			}
		}
		return(1);
	}
}

// Returns the next non-unit benefit of the technology, using values as in infotbar
char nexttechbenefit(char curbenefit, struct TECH *tech)
{
	if (tech->gold && curbenefit < 0)
	{
		return(0);
	}
	else if (tech->prod5 && curbenefit < 1)
	{
		return(1);
	}
	else if (tech->sci5 && curbenefit < 2)
	{
		return(2);
	}
	else if (tech->bridge && curbenefit < 3)
	{
		return(3);
	}
	else
	{
		return(4);
	}
}

/*char trykey(short k, short offset, struct KEYS *keys)
{
	if (k == KEY_UP + offset)
	{
		return(1);
	}
	else if (k == KEY_DOWN + offset)
	{
		return(4);
	}
	else if (k == KEY_LEFT + offset)
	{
		return(2);
	}
	else if (k == KEY_RIGHT + offset)
	{
		return(5);
	}
	else
	{
		return(0);
	}
}*/

// Returns (1 for dir, 2 for 2nd + dir, 3 for diamond + dir) + 3 if down
char dirkeys(short k)
{
	/*char a;
	a = trykey(k, 0, keys);
	if (a)
	{
		return(a);
	}
	a = trykey(k, _2nd, keys);
	if (a)
	{
		return(a + ((a % 3) == 1));
	}
	a = trykey(k, keys->diamond, keys);
	if (a)
	{
		return(a + ((a % 3) == 1) + 1);
	}
	return(0);*/
	char a, b;
	a = dirkeys2(k);
	if (a == 2)
	{
		return(1);
	}
	else if (a == 4)
	{
		return(4);
	}
	else
	{
		b = dirkeys2(k - _2nd);
		if (a == 1 || b == 1 || b == 2)
		{
			return(2);
		}
		else if (a || b)
		{
			return(5);
		}
		else
		{
			a = dirkeys2(k - KEY_DIAMOND);
			if (a == 1 || a == 2)
			{
				return(3);
			}
			else if (a)
			{
				return(6);
			}
			else
			{
				return(0);
			}
		}
	}
}

void drawinfobox(unsigned char selected, unsigned char start, unsigned char end, char istechnology)
{
	char y;
	if (selected != 255)
	{
		if (istechnology)
		{
			if (end <= 14 || selected <= 7 || selected == start)
			{
				y = selected;
			}
			else //if (selected + 7 >= end)
			{
				y = max(14 + selected - end, 7);
			}
			/*else
			{
				y = 7;
			}*/
			y = 6 * y + 8;
		}
		else
		{
			y = 28;
		}
	}
	else
	{
		y = 0;
	}
	drawrect100159(y, 6);
}

static inline void swordshield(unsigned char x, char y, void *plane)
{
	Sprite8(x, y, 7, (unsigned char[]){0x40,0x40,0x40,0x40,0xE0,0x40,0x40}, plane, SPRT_OR);
	Sprite8(x + 20, y, 7, (unsigned char[]){0xF8,0xF8,0xF8,0xF8,0xF8,0x70,0x20}, plane, SPRT_OR);
}

unsigned char max1(unsigned char a)
{
	return(max(a, 1));
}

// gives information about a technology or unit
// istechnology indicates whether the information is regarding a technological advance rather than a unit
void infotbar(struct PLAYER *curplyr, struct TECH *tech, struct UNITTYPE *info, struct NUMS *nums, struct SORTS *sorts, char istechnology, char index, char grayscale)
{
	// cat means category
	// if istechnology = 1, then selected is the line number of the current selection
	// end is the last line of display, whereas end2 is the greatest value selected can assume
	// start is the lowest value selected can assume
	unsigned char e, h, start, selected, prevselected, end, end2, cat1, cat2, recalc, redraw; //, swordshield[] = {0x40,0x40,0x40,0x40,0xE0,0x40,0x40,0xF8,0xF8,0xF8,0xF8,0xF8,0x70,0x20};
	char a, b, d, f, y, s[18];
	short c, k;
	void *plane;
	struct UNITTYPE *curinfo;
	struct TECH *curtech, *curtech2;
	recalc = redraw = 1;
	if (grayscale)
	{
		plane = GetPlane(DARK_PLANE);
	}
	else
	{
		plane = LCD_MEM;
	}
	do
	{
		if (recalc)
		{
			// istechnology = 1 is assumed
			cat1 = start = 0;
			curtech = tech + index;
			for(a = -1; a <= 3; cat1++)
			{
				a = nexttechbenefit(a, curtech);
			}
			for(a = 0; a < nums->infos; a++)
			{
				if (info[a].tech == index)
				{
					cat1++;
					if (!start)
					{
						start = cat1;
					}
				}
			}
			for(cat2 = 0; cat2 <= 2; cat2++)
			{
				if (curtech->prereq[cat2] == 255)
				{
					break;
				}
			}
			cat1--;
			// 4 = 5 - 1
			c = 4;
			for(a = 0; a < nums->techs; a++)
			{
				for(b = 0; b <= 2; b++)
				{
					e = tech[a].prereq[b];
					//curtech2 = tech + a;
					if (e == index)
					{
						c++;
					}
					else if (e == 255)
					{
						break;
					}
				}
			}
			end2 = end = max1(cat1) + max1(cat2) + max(c, 5);
			if (c < 5)
			{
				if (cat2)
				{
					//end2 = max1(cat1) + cat2 + 3;
					end2 -= 2;
				}
				else
				{
					//end2 = cat1 + 2;
					end2 -= 4;
				}
			}
			if (!start++)
			{
				start = 4 + max1(cat1);
				if (!cat2)
				{
					if (c)
					{
						start += 2;
					}
					else
					{
						start = 255;
					}
				}
			}
			selected = start;
			prevselected = 255;
			recalc = 0;
		}
		if (redraw)
		{
			a = ((end > 14 && max(selected, 7) != max(prevselected, 7) && min(end, selected + 7) != min(end, prevselected + 7)) || !istechnology || prevselected == 255);
			if (a)
			{
				if (grayscale)
				{
					cleartbar();
					setdarkplane();
				}
				else
				{
					revrect(100, 0, 159, 99);
				}
				setfont4x6();
			}
			/*char inf[30];
			sprintf(inf, "%d  %d     ", selected, end);
			DrawStr(0, 90, inf, A_REPLACE);*/
			if (istechnology)
			{
				if (a)
				{
					a = b = 0;
					f = 1;
					if (selected <= 7 || end <= 14 || selected == start)
					{
						c = -4;
						y = 9;
						d = 14;
					}
					else
					{
						c = min(selected + 7, end) - 17;
						if (c + 2 <= max1(cat1))
						{
							if (c >= 0)
							{
								for(d = -1; d <= 3 && c >= 0; f++)
								{
									d = nexttechbenefit(d, curtech);
									c--;
								}
								if (c >= 0)
								{
									f += ++c;
									for(d = 0; c >= 0; d++)
									{
										if (info[sorts->asort[d]].tech == index)
										{
											c--;
										}
									}
									c = d + 4;
								}
								else
								{
									c = d;
								}
							}
						}	
						else
						{
							c -= max1(cat1);
							if (c < max1(cat2))
							{
								b = 1;
							}
							else
							{
								f = (c -= (max1(cat2) + 1));
								if (c >= 0)
								{
									for(d = 0; d < nums->techs; d++)
									{
										//curtech2 = tech + sorts->tsort[d];
										for(b = 0; b <= 2; b++)
										{
											e = tech[d].prereq[b];
											if (e == 255)
											{
												break;
											}
											else if (e == index)
											{
												if (!c--)
												{
													c = d;
													d = 126;
													break;
												}
											}
										}
									}
								}
								/*else
								{
									f = 0;
								}*/
								b = 2;
							}
						}
						DrawCharXor(100, 9, UPCHAR);
						d = 13;
						y = 15;
					}
					DrawStrXor100(1, curtech->id);
					DrawLineXor(100, 7, 159, 7);
					if (end <= 14)
					{
						d = end;
					}
					else if (selected + 7 < end)
					{
						DrawCharXor(100, 93, DOWNCHAR);
						d--;
					}
					/*sprintf(s, "%d  ", selected);
					DrawStr(0, 91, s, A_REPLACE);
					sprintf(s, "%d  ", f);
					DrawStr(22, 91, s, A_REPLACE);*/
					do
					{
						switch(b)
						{
							case 0:
								if (c >= -1)
								{
									if (cat1)
									{
										if (c <= 3)
										{
											c = nexttechbenefit(c, curtech);
										}
										if (c > 3)
										{
											do
											{
												curinfo = info + sorts->asort[++c - 5];
											}
											while (curinfo->tech != index);
											strcpy(s, curinfo->id);
										}
										else
										{
											switch(c)
											{
												case 0:
													sprintf(s, "Revenue +%d", curtech->gold);
													break;
												case 1:
													sprintf(s, "Production +%d%%", 5 * curtech->prod5);
													break;
												case 2:
													sprintf(s, "Science +%d%%", 5 * curtech->sci5);
													break;
												default:
													strcpy(s, "Bridge Building");
											};
										}
										/*if (f++ < cat1)
										{
											strcat(s, ",");
										}
										else*/
									}
									else
									{
										strcpy(s, "(none)");
										f = 0;
										/*b = 1;
										c = -1;*/
									}
									if (f++ >= cat1)
									{
										b = 1;
										c = -1;
									}
								}
								else
								{
									if (c == -4)
									{
										strcpy(s, "Researched");
										/*if (curplyr->researched[index])
										{
											strcpy(s, "Researched");
										}
										else*/ if (curplyr->researching == index)
										{
											e = (char)(curplyr->researchp40 / (short)(80 + 20 * curtech->age));
											//strcpy(s, "Researching");
											strcpy(s + 8, "ing");
											if (e <= 9)
											{
												strcat(s, " ");
											}
											sprintf(s + strlen(s), "(%d%%)", e);
										}
										else if (!curplyr->researched[index])
										{
											strcpy(s, "Not Researched");
										}
									}
									else if (c == -3)
									{
										strcpy(s, "Type: ");
										switch(curtech->age)
										{
											case 0:
												strcat(s, "Ancient");
												break;
											case 1:
												strcat(s, "Basic");
												break;
											case 2:
												strcat(s, "Industrial");
												break;
											default:
												strcat(s, "Modern");
										};
									}
									else
									{
										*s = 0;
										DrawStrXor(115, y, "Benefits:");
									}
									c++;
								}
								break;
							case 1:
								if (c >= 0)
								{
									if ((f = cat2))
									{
										strcpy(s, tech[curtech->prereq[c]].id);
										/*if ((f = (c + 1 < cat2)))
										{
											strcat(s, ",");
										}
										else if (!(f = (c + 1 < cat2)))
										{
											b = 2;
											c = -2;
										}*/
									}
									else
									{
										strcpy(s, "(none)");
										/*b = 2;
										c = -2;*/
									}
									if (!(f = (c + 1 < cat2)))
									{
										b = 2;
										c = -2;
									}
								}
								else
								{
									strcpy(s, "   Prerequisites:");
								}
								c++;
								break;
							default:
								*s = 0;
								if (c >= 0)
								{
									do
									{
										curtech2 = tech + c;
										for(e = 0; e <= 2; e++)
										{
											h = curtech2->prereq[e];
											if (h == 255)
											{
												break;
											}
											else if (h == index)
											{
												strcpy(s, curtech2->id);
												break;
											}
										}
										c++;
									}
									while (c < nums->techs && !*s);
									if (!*s)
									{
										strcpy(s, "(none)");
									}
									if (++f + max1(cat1) + max1(cat2) + 4 >= end)
									{
										c = -1;
									}
									/*else if (++f + max1(cat1) + max1(cat2) + 4 < end)
									{
										strcat(s, ",");
									}*/
								}
								else
								{
									DrawStrXor(117, y, "Allows:");
									c++;
								}
						};
						if (c >= 1 || (!b && !c))
						{
							strcat(s, ",");
						}
						DrawStrXor100(y, s);
						y += 6;
					}
					while (++a <= d); //&& a + selected <= end);
				}
				else
				{
					drawinfobox(prevselected, start, end, istechnology);
				}
				prevselected = selected;
			}
			else
			{
				curinfo = info + index;
				sprintfd(s, curinfo->a);
				DrawStrXor100(1, curinfo->id);
				DrawLineXor(100, 7, 159, 7);
				swordshield(105, 9, plane);
				setfont6x8();
				DrawStrXor(108, 9, s);
				sprintfd(s, curinfo->d);
				DrawStrXor(130, 9, s);
				// Sprite8or is not used because grayscale may be off
				Sprite8(147, 9, 8, sprites + ((index + 6) << 3), plane, SPRT_OR);
				setfont4x6();
				sprintf(s, "Moves: %d", curinfo->m);
				DrawStrXor100(17, s);
				DrawStrXor(108, 23, "Prerequisite:");
				if (curinfo->tech != 63)
				{
					DrawStrXor100(29, tech[curinfo->tech].id);
					selected = 0;
				}
				else
				{
					DrawStrXor100(29, "(none)");
					selected = 255;
				}
				strcpy(s, "Type: ");
				switch(curinfo->type)
				{
					case gro:
						strcat(s, "Ground");
						break;
					case wat:
						strcat(s, "Water");
						break;
					case air:
						strcat(s, "Air");
						break;
					case ha:
						strcat(s, "High Air");
						break;
					case bom:
						strcat(s, "Bomb");
						break;
					//case bmr:
					default:
						strcat(s, "Bomber");
				};
				DrawStrXor100(y = 35, s);
				if (curinfo->trans)
				{
					sprintf(s, "Trans: %d", curinfo->trans);
					a = 0;
					if (!curinfo->transbmr)
					{
						if (curinfo->transgro)
						{
							strcat(s, " ground");
							a++;
						}
						if (curinfo->transwat)
						{
							if (a)
							{
								strcat(s, ",");
								DrawStrXor100(y += 6, s);
								strcpy(s, " ");
							}
							strcat(s, " water");
							a++;
						}
						if (curinfo->transair)
						{
							if (a)
							{
								strcat(s, ",");
								if (a == 1)
								{
									DrawStrXor100(y += 6, s);
									strcpy(s, " ");
								}
							}
							strcat(s, " air");
							a++;
						}
						if (curinfo->transha)
						{
							if (a)
							{
								strcat(s, ",");
								if (a == 1)
								{
									DrawStrXor100(y += 6, s);
									strcpy(s, " ");
								}
							}
							if (a < 3)
							{
								strcat(s, " high air");
							}
							else
							{
								strcat(s, " h.air");
								memmove(s, s + 1, 19);
							}
						}
					}
					DrawStrXor100(y += 6, s);
				}
				if (curinfo->br)
				{
					sprintf(s, "Blast Radius: %d", curinfo->br);
					DrawStrXor100(y += 6, s);
				}
				if (curinfo->buildmult10)
				{
					sprintf(s, "Build Speed: %d.%d", curinfo->buildmult10 / 10, curinfo->buildmult10 % 10);
					DrawStrXor100(y += 6, s);
				}
				if (curinfo->canals)
				{
					DrawStrXor100(y += 6, "Can Build Canals");
				}
				if (curinfo->mines)
				{
					sprintf(s, "Mines: %d gold", curinfo->mines);
					DrawStrXor100(y += 6, s);
				}
				if (curinfo->assist5)
				{
					sprintf(s, "Assists City: %d%%", curinfo->assist5 * 5);
					DrawStrXor100(y += 6, s);
				}
				/*if (!curinfo->bribe)
				{*/
					if (curinfo->a)
					{
						sprintf(s, "Vs. Air: %d%%", curinfo->vsa5 * 5);
						DrawStrXor100(y += 6, s);
						sprintf(s + 4, "High Air: %d%%", curinfo->vsha5 * 5);
						DrawStrXor100(y += 6, s);
					}
				/*}
				else
				{
					DrawStrXor100(y += 6, "Can Bribe");
				}*/
				if (y <= 83)
				{
					if (curinfo->cau)
					{
						DrawStrXor100(y += 6, "Dstroyd After Use");
					}
					if (y <= 83)
					{
						sprintf(s, "Takes: %d.%d turns", curinfo->turns10 / 10, curinfo->turns10 % 10);
						DrawStrXor100(y + 6, s);
						if (y <= 83)
						{
							sprintf(s, "Costs: %d gold", 10 * curinfo->cost10);
							DrawStrXor100(y + 12, s);
						}
					}
				}
			}
			drawinfobox(selected, start, end, istechnology);
			redraw = 0;
		}
		if ((a = dirkeys(k = unknownngetchx())))
		{
			if (selected != 255 && istechnology)
			{
				if (a >= 4)
				{
					switch(a)
					{
						case 4:
							a = 2;
							break;
						case 5:
							a = 11;
							break;
						//case 6:
						default:
							a = 127;
					};
					for(a--; a; a--)
					{
						if (selected == end2)
						{
							break;
						}
						else
						{
							b = (selected != cat1 + 2);
							if (b && selected != max1(cat1) + cat2 + 3)
							{
								selected++;
							}
							else if (b || cat2)
							{
								selected += 2;
							}
							else
							{
								selected += 4;
							}
						}
						redraw = 1;
					}
				}
				else
				{
					switch(a)
					{
						case 2:
							a = 10;
							break;
						// The following line must not read "default:"
						case 3:
						//default:
							a = 127;
					};
					for(d = 1; d <= a; d++)
					{
						if (selected == start)
						{
							break;
						}
						else
						{
							b = (selected != max1(cat1) + max1(cat2) + 5);
							if (selected != max1(cat1) + 4 && b)
							{
								selected--;
							}
							else if (b || cat2)
							{
								selected -= 2;
							}
							else
							{
								selected -= 4;
							}
						}
						redraw = 1;
					}
				}
			}
		}
		else if (k == 13)
		{
			if (selected != 255)
			{
				if (istechnology)
				{
					b = selected - (max1(cat1) + 4);
					if (selected <= cat1 + 2)
					{
						b = selected - start + 1;
						for(a = 0; b; a++)
						{
							c = sorts->asort[a];
							if (info[c].tech == index)
							{
								b--;
							}
						}
						index = c;
						istechnology = 0;
					}
					//else if (selected <= max(cat1, 1) + cat2 + 3)
					else if (b < cat2)
					{
						index = curtech->prereq[b];
					}
					else
					{
						b -= max(cat2, 1);
						for(a = 0; b; a++)
						{
							//curtech2 = tech + sorts->tsort[a];
							//d = sorts->tsort[a];
							for(c = 0; c <= 2; c++)
							{
								e = tech[a].prereq[c];
								if (e == index)
								{
									b--;
								}
								else if (e == 255)
								{
									break;
								}
							}
						}
						index = a - 1;
					}
					redraw = 1;
				}
				else
				{
					index = curinfo->tech;
					redraw = istechnology = 1;
				}
				recalc = istechnology;
			}
		}
		else if ((a = keyletter(k)))
		{
			/*if ((recalc = istechnology))
			{
				b = nums->techs;
				g = sorts->tsort;
			}
			else
			{
				b = nums->infos;
				g = sorts->asort;
			}
			for(a = 0; g[a] != index; a++);*/
			if ((recalc = istechnology))
			{
				c = nums->techs;
			}
			else
			{
				c = nums->infos;
			}
			if (k == '+' || k == '-')
			{
				if (istechnology)
				{
					a = index;
					if (k == '+')
					{
						index++;
					}
					else
					{
						index += c - 1;
					}
					index %= c;
				}
				else
				{
					for(a = 0; sorts->asort[a] != index; a++);
					if (k == '+')
					{
						a++;
					}
					else
					{
						a += c - 1;
					}
					index = sorts->asort[a % c];
				}
			}
			else
			{
				b = 0;
				do
				{
					if (istechnology)
					{
						d = *(tech[index = b].id);
					}
					else
					{
						d = *(info[index = sorts->asort[b]].id);
					}
					b++;
				}
				while (a > tolower(d) && b < c);
			}
			redraw = 1;
		}
	}
	while (k != 264 && (k != 4361 || grayscale));
	drawinfobox(selected, start, end, istechnology);
}

void drawgettechbox(unsigned char selected, unsigned char start)
{
	char a;
	a = 9 * (selected + (start != 0));
	drawrect0(a + 23, 98, a + 31);
}

// Updates the apos array
void redoapos(struct PLAYER *curplyr, struct NUMS *nums, struct SORTS *sorts)
{
	char a, b, c;
	for(a = b = 0; a < nums->infos; a++)
	{
		if (curplyr->active[a])
		{
			b++;
			/*DrawChar(0, 50, a + 'a', A_REPLACE);
			ngetchx();*/
		}
	}
	tryfree(curplyr->apos);
	/*DrawCharXor(0, random(100), b + 'a');
	ngetchx();*/
	curplyr->apos = (char *)malloc_throw(b + 1);
	for(a = b = 0; a < nums->infos; a++)
	{
		if (curplyr->active[c = sorts->asort[a]])
		{
			curplyr->apos[b++] = c;
		}
	}
	curplyr->apos[b] = 127;
}

/*void centertext(const char *s, char y)
{
	DrawStrXor(80 - strlen(s) * 3, y, s);
}*/

char f1f2()
{
	short k;
	do
	{
		k = unknownngetchx();
	}
	while (k != 268 && k != 269);
	return(k == 268);
}

void DrawStrXor1(char y, const char *s)
{
	DrawStrXor(1, y, s);
}

char planelesstbarmessage(const char *s)
{
	char *a, b[78], *c, y;
	strcpy(c = b, s);
	y = 1;
	while ((a = strchr(c, '^')))
	{
		*a = 0;
		DrawStrXor100(y, c);
		y += 8;
		c = a + 1;
	}
	DrawStrXor100(y, c);
	return(y + 12);
}

char tbarmessage(const char *s)
{
	setfont6x8();
	cleartbar();
	setdarkplane();
	return(planelesstbarmessage(s));
}

// gets the next technological advance from the user
void gettechnology(struct PLAYER *curplyr, struct TECH *tech, struct UNITTYPE *info, struct NUMS *nums, struct SORTS *sorts, char goalseekonly)
{
	short k;
	// pre means prerequisite
	char a, b, c, e, goalseek, start, prevstart, prevselected, end, end2, selected, toptpos, redraw, *pre, s[18];
	unsigned char d;
	a = end = nums->techs - 1;
	if (!(goalseek = goalseekonly))
	{
		for(end = -1; a >= 0; a--)
		{
			if (techavailable(curplyr, tech, a))
			{
				toptpos = a;
				end++;
			}
		}
	}
	else
	{
		toptpos = 0;
	}
	GrayOff();
	ClrScr();
	if (end >= 0)
	{
		redraw = prevstart = 1;
		start = selected = 0;
		DrawLineXor(99, 0, 99, 99);
		do
		{
			if (redraw)
			{
				if (prevstart != start)
				{
					revrect(0, 0, 98, 99);
					setfont4x6();
					if (!goalseek)
					{
						b = 9;
						DrawStrXor(43, 95, "F2: Goal Seek");
						setfont6x8();
						DrawStrXor(10, 1, "What will you");
						strcpy(s, "research, ");
						if (turn)
						{
							strcat(s, "gray?");
							a = 4;
						}
						else
						{
							strcat(s, "black?");
							a = 1;
						}
						DrawStrXor(a, 10, s);
					}
					else
					{
						if (!goalseekonly)
						{
							b = 7;
							DrawStrXor(39, 95, "F2: Select Tech");
						}
						else
						{
							b = 37;
						}
						setfont6x8();
						DrawStrXor(4, 1, "Goal seek which");
						DrawStrXor(16, 9, "technology?");
					}
					setfont4x6();
					DrawStrXor(b, 95, "F1: Info");
					setfont6x8();
					if (start)
					{
						DrawCharXor(1, 23, UPCHAR);
					}
					if (start + 6 + !start < end)
					{
						DrawCharXor(1, 86, DOWNCHAR);
						c = 6;
					}
					else
					{
						c = 7;
					}
					c = min(c, end + (start != 0) - start);
					b = toptpos;
					for(a = (start != 0); a <= c; a++)
					{
						DrawStrXor1(a * 9 + 24, tech[b].id);
						if (!goalseek)
						{
							if (a < c) //&& a + start < end + (start != 0))
							{
								while (!techavailable(curplyr, tech, ++b));
							}
						}
						else
						{
							b++;
						}
					}
				}
				else
				{
					drawgettechbox(prevselected, start);
				}
				drawgettechbox(prevselected = selected, prevstart = start);
				redraw = 0;
			}
			a = dirkeys(k = nograyngetchx());
			if (a)
			{
				if (a >= 4)
				{
					switch(a)
					{
						case 4:
							a = 2;
							break;
						case 5:
							a = 8;
							break;
						default:
							a = 127;
					};
					for(a--; a; a--)
					{
						if (selected + start == end)
						{
							break;
						}
						else
						{
							redraw = 1;
							if (selected == 5 + !start && selected + start + 1 != end)
							{
								if (!start++)
								{
									start++;
									selected--;
								}
							}
							else
							{
								selected++;
							}
						}
					}
				}
				else
				{
					switch(a)
					{
						case 2:
							a = 7;
							break;
						// The following line must not be "default:"
						case 3:
							a = 127;
					};
					for(b = 1; b <= a; b++)
					{
						if (!selected && !start)
						{
							break;
						}
						else
						{
							redraw = 1;
							if (!selected)
							{
								if (start != 2)
								{
									start--;
								}
								else
								{
									start = 0;
									selected = 1;
								}
							}
							else
							{
								selected--;
							}
						}
					}
				}
				if (!goalseek)
				{
					if (a < start)
					{
						b = 1;
					}
					else
					{
						b = -1;
					}
					for(a = prevstart; a != start; a += b)
					{
						while (!techavailable(curplyr, tech, toptpos += b));
					}
				}
				else
				{
					toptpos = start;
				}
			}
			else if (k == 268)
			{
				b = toptpos;
				if (!goalseek)
				{
					for(a = 1; a <= selected; a++)
					{
						while(!techavailable(curplyr, tech, ++b));
					}
				}
				else
				{
					b += selected;
				}
				drawgettechbox(selected, start);
				infotbar(curplyr, tech, info, nums, sorts, 1, b, 0);
				drawgettechbox(selected, start);
			}
			else if ((k == 269 && !goalseekonly) || (k == 264 && goalseek))
			{
				if (!goalseekonly)
				{
					selected = start = toptpos = 0;
					if ((goalseek = !goalseek))
					{
						end2 = end;
						end = nums->techs - 1;
					}
					else
					{
						end = end2;
						while(!techavailable(curplyr, tech, toptpos))
						{
							toptpos++;
						}
					}
					redraw = prevstart = 1;
				}
				else
				{
					k = -1;
				}
			}
			else if (k == 13)
			{
				if (!goalseek)
				{
					b = selected;
					a = toptpos;
					while (b--)
					{
						while (!techavailable(curplyr, tech, ++a));
					}
					setfont6x8();
					revrect(0, 0, 98, 99);
					DrawStrXor1(1, "Confirm");
					DrawStrXor1(10, "researching");
					DrawStrXor1(19, tech[a].id);
					DrawStrXor1(33, "F1: Confirm");
					DrawStrXor1(42, "F2: Change");
					if (!f1f2())
					{
						redraw = 1;
						prevstart = -1;
						k = 0;
					}
				}
				else
				{
					setfont4x6();
					revrect(100, 0, 159, 99);
					sprintf(s, "%s:", tech[a = selected + start].id);
					DrawStrXor100(1, s);
					setfont6x8();
					if (curplyr->researched[a])
					{
						planelesstbarmessage("^You have^already^developed^this.");
					}
					else if (curplyr->researching == a && goalseekonly)
					{
						planelesstbarmessage("^You are^currently^develop-^ing this.");
					}
					else
					{
						pre = (char *)malloc_throw(nums->techs);
						for(b = 0; b < nums->techs; b++)
						{
							pre[b] = 0;
						}
						pre[a] = 2;
						do
						{
							c = 0;
							for(e = 0; e < nums->techs; e++)
							{
								if (pre[e] == 2)
								{
									for(b = 0; b <= 2; b++)
									{
										d = tech[e].prereq[b];
										if (d == 255)
										{
											break;
										}
										else if (pre[d] != 1)
										{
											pre[d] = 2;
											c = 1;
										}
									}
									pre[e] = 1;
								}
							}
						}
						while (c);
						for(b = nums->techs - 1; b >= 0; b--)
						{
							if (pre[b])
							{
								if (techavailable(curplyr, tech, b))
								{
									break;
								}
							}
						}
						setfont4x6();
						e = -1;
						for(c = 0; c <= 11 && e < b; c++)
						{
							for(e++; e <= b; e++)
							{
								//d = sorts->tsort[e];
								if (pre[e])
								{
									if (techavailable(curplyr, tech, e))
									{
										break;
									}
								}
							}
							strcpy(s, tech[e].id);
							if (b != e)
							{
								//sprintf(s, "%s,", tech[sorts->tsort[e]].id);
								strcat(s, ",");
								if (!c)
								{
									DrawStrXor100(7, "Research one of");
									DrawStrXor100(13, "these:");
								}
							}
							else if (!c)
							{
								DrawStrXor100(7, "Research this:");
								DrawStrXor100(16, s);
								break;
							}
							DrawStrXor100(c * 6 + 22, s);
						}
						if (b != e)
						{
							DrawStrXor100(94, "(more not shown)");
						}
						tryfree(pre);
					}
				}
			}
			else if ((a = keyletter(k)))
			{
				toptpos = 0;
				if (!goalseek)
				{
					//for(toptpos = 0; !techavailable(curplyr, tech, toptpos); toptpos++);
					while(!techavailable(curplyr, tech, toptpos))
					{
						toptpos++;
					}
				}
				for(start = 0; start < end && tolower(*(tech[toptpos].id)) < a; start++)
				{
					if (!goalseek)
					{
						while(!techavailable(curplyr, tech, ++toptpos));
					}
					else
					{
						toptpos++;
					}
				}
				if (end <= 7)
				{
					selected = start;
					start = 0;
				}
				else if ((selected = max(start + 6 - end, 0)))
				{
					start = end - 6;
				}
				if (start + selected > end)
				{
					start = end - selected;
				}
				else if (start == 1)
				{
					start = 0;
					selected = 1;
				}
				if (selected)
				{
					if (!goalseek)
					{
						for(b = 0; b < selected; b++)
						{
							while(!techavailable(curplyr, tech, --toptpos));
						}
					}
					else
					{
						toptpos -= selected;
					}
				}
				redraw = 1;
			}
		}
		while ((k != 13 || goalseek) && k != -1);
		if (!goalseekonly)
		{
			curplyr->researching = a;
			curplyr->researchp40 = 0;
		}
	}
	else if (!goalseekonly)
	{
		curplyr->researching = 255;
		bigmessage("All technologies^have been^developed!");
		etc();
	}
}

short science(struct PLAYER *curplyr)
{
	short a;
	char b;
	a = (40 + (curplyr->rbonus5 << 1)) * (curplyr->cities + 10);
	for(b = 0; b < curplyr->cities; b++)
	{
		//a += (40 + (curplyr->rbonus5 << 1));
		if (curplyr->city[b].building == 254)
		{
			a += (60 + 3 * curplyr->rbonus5);
		}
	}
	return(a);
}

/*void addgold(char *s)
{
	strcat(s, " gold");
}*/

void turnstr(char *s, struct NUMS *nums)
{
	//strcpy(s, "Turn ");
	//comma(s, nums->turnnum);
	sprintf(s, "Turn %d", nums->turnnum);
	if (nums->maxturn)
	{
		//strcat(s, " / ");
		//comma(s, nums->maxturn);
		sprintf(s + strlen(s), " / %d", nums->maxturn);
	}
}

char reallyabandon()
{
	bigmessage("Really abandon this^game?");
	DrawStrXor1(28, "F1: Yes");
	DrawStrXor1(39, "F2: No");
	return(f1f2());
}

void emptyfull(char a, char y)
{
	FILE *input;
	char b[14];
	sprintf(b, "main\\civgame%d", a);
	revrect(81, y, 119, y + 7);
	if ((input = fopen(b, "rb")))
	{
		fclose(input);
		//strcpy(b, "(full)");
		DrawStrXor(79, y, "(full)");
	}
	else
	{
		//strcpy(b, "(empty)");
		DrawStrXor(79, y, "(empty)");
	}
	//DrawStrXor(79, y, b);
}

static inline char savegame(struct PLAYER *plyr, struct MAP *map, struct NUMS *nums, char *scenname)
{
	FILE *output;
	char fname[14], b;
	short a;
	sprintf(fname, "main\\civgame%d", nums->game);
	//unlink(fname);
	if (!(output = fopen(fname, "wb")))
	{
		return(0);
	}
	// a represents the version number.  The purpose of writing the version number to the save file is to
	// make newer versions of civ89, which may save information in a different format, compatible with older
	// save files.
	a = 104;
	if (!fwrite(&a, 2, 1, output))
	{
		return(0);
	}
	if (!fwrite(nums, sizeof(struct NUMS), 1, output))
	{
		return(0);
	}
	// The players' names are written here for easy access in the askwhichdatatoload function
	for(a = 0; a <= 1; a++)
	{
		if (fwrite(nums->player[a], 1, 11, output) != 11)
		{
			return(0);
		}
	}
	if (!fwrite(&turn, 1, 1, output))
	{
		return(0);
	}
	if (fwrite(plyr, sizeof(struct PLAYER), 2, output) != 2)
	{
		return(0);
	}
	if (fwrite(scenname, 1, 21, output) != 21)
	{
		return(0);
	}
	for(a = 0; a <= 1; a++)
	{
		b = aposend(plyr) - 1;
		if (fwrite(plyr->unit, sizeof(struct UNIT), plyr->units, output) != plyr->units) //|| fwrite(nums->player[a], 1, 11, output) != 11)
		{
			return(0);
		}
		if (fwrite(plyr->city, sizeof(struct CITY), plyr->cities, output) != plyr->cities)
		{
			return(0);
		}
		if (fwrite(plyr->researched, 1, nums->techs, output) != nums->techs)
		{
			return(0);
		}
		if (fwrite(plyr->active, 1, nums->infos, output) != nums->infos)
		{
			return(0);
		}
		if (!fwrite(&b, 1, 1, output))
		{
			return(0);
		}
		if (fwrite(plyr->apos, 1, b, output) != b)
		{
			return(0);
		}
		plyr++;
	}
	a = tiles(map);
	// b is whether the previous save was from edit mode as opposed to play mode
	b = 0;
	if (!fwrite(map, sizeof(struct MAP), 1, output))
	{
		return(0);
	}
	if (fwrite(map->tile, sizeof(struct TILE), a, output) != a)
	{
		return(0);
	}
	if (!fwrite(&b, 1, 1, output))
	{
		return(0);
	}
	fputc(0, output);
	fputs("CIV", output);
	fputc(0, output);
	fputc(OTH_TAG, output);
	return(!fclose(output));
}

void freeplyr(struct PLAYER *plyr)
{
	char a;
	for(a = 0; a <= 1; a++)
	{
		tryfree(plyr->unit);
		tryfree(plyr->city);
		tryfree(plyr->active);
		tryfree(plyr->researched);
		tryfree(plyr->apos);
		plyr++;
	}
}

char loadgame(struct PLAYER *plyr, struct MAP *map, struct NUMS *nums, char *scenname)
{
	FILE *input;
	char fname[14], b, c, d;
	short a;
	unsigned short code2[8];
	c = nums->game;
	sprintf(fname, "main\\civgame%d", c);
	if (!(input = fopen(fname, "rb")))
	{
		return(0);
	}
	// Note that it is necessary to free unit, city, and player
	freeplyr(plyr);
	for(a = 0; a <= 1; a++)
	{
		tryfree(nums->player[a]);
	}
	tryfree(nums->player);
	tryfree(map->tile);
	
	//Read the version number of the game.  This used to be equal to the version number of Civ89 times 100.
	//Saved game files corresponding to this version of Civ89 have version number 104.
	//Now, a / 100 represents the major version number, while a % 100 represents the minor version number.  If the major version number is greater than 1, it refuses to load.  It will not refuse to load files with a major version number of 1 and a minor version number higher than 4.  This might allow for future usage of unused bits without forcing backwards incompatability.
	if (freadc(&a, 2, 1, input))
	{
		return(0);
	}
	
	//Refuse to load games with a major version number of 2 or greater.
	if (a >= 200)
	{
		return 0;
	}
	
	if (freadc(nums, sizeof(struct NUMS), 1, input))
	{
		return(0);
	}
	// The players' names are read early for easy access in the askwhichdatatoload function
	nums->game = c;
	nums->player = (char **)malloc_throw(sizeof(char *) << 1);
	for(d = 0; d <= 1; d++)
	{
		if (fread1(nums->player[d] = (char *)malloc_throw(11), 11, input))
		{
			return(0);
		}
	}
	//*plyr = (struct PLAYER *)malloc_throw(sizeof(struct PLAYER) << 1);
	if (fread1(&turn, 1, input))
	{
		return(0);
	}
	if (freadc(plyr, sizeof(struct PLAYER), 2, input))
	{
		return(0);
	}
	if (fread1(scenname, 21, input))
	{
		return(0);
	}
	for(d = 0; d <= 1; d++)
	{
		plyr->unit = (struct UNIT *)malloc_throw(300 * sizeof(struct UNIT));
		plyr->city = (struct CITY *)malloc_throw(50 * sizeof(struct CITY));
		plyr->active = (unsigned char *)malloc_throw(nums->infos);
		plyr->researched = (unsigned char *)malloc_throw(nums->techs);
		if (freadc(plyr->unit, sizeof(struct UNIT), plyr->units, input))
		{
			return(0);
		}
		if (a > 100)
		{
			if (freadc(plyr->city, sizeof(struct CITY), plyr->cities, input))
			{
				return(0);
			}
		}
		else if (readandcopycities(&input, plyr))
		{
			return(0);
		}
		if (fread1(plyr->researched, nums->techs, input))
		{
			return(0);
		}
		if (fread1(plyr->active, nums->infos, input))
		{
			return(0);
		}
		if (fread1(&b, 1, input))
		{
			return(0);
		}
		/*char inf[30];
		ClrScr();
		sprintf(inf, "%d  %d  %d  %d", curplyr->city[0].x, curplyr->city[0].y, curplyr->city[1].x, curplyr->city[1].y);
		DrawStrXor(0, 80, inf);
		ngetchx();*/
		plyr->apos = (char *)malloc_throw(b);
		//nums->player[a] = (char *)malloc_throw(11);
		if (fread1(plyr->apos, b, input)) //|| fread(nums->player[a], 1, 11, input) != 11)
		{
			return(0);
		}
		plyr++;
	}
	if (freadc(map, sizeof(struct MAP), 1, input))
	{
		return(0);
	}
	a = tiles(map);
	map->tile = (struct TILE *)malloc_throw(a * sizeof(struct TILE));
	if (freadc(map->tile, sizeof(struct TILE), a, input))
	{
		return(0);
	}
	if (fread1(&b, 1, input))
	{
		return(0);
	}
	if (fclose(input))
	{
		return(0);
	}
	if (b)
	{
		// The following code assumes that grayscale is off
		bigmessage("This game has been^edited since the^last save.");
		drawrect0(0, 159, 99);
		delay(40);
		/*while (kbhit())
		{
			ngetchx();
		}*/
		GKeyFlush();
		etc();
	}
	for(b = 0; b <= 9; b++)
	{
		sprintf(fname, "main\\scen%d", b);
		if ((input = fopen(fname, "rb")))
		{
			if (!fseek(input, 21, SEEK_SET))
			{
				if (!freadc(code2, 2, 8, input))
				{
					/*for(a = 0; a <= 7; a++)
					{
						if (nums->code[a] != code2[a])
						{
							break;
						}
					}
					if (a == 8)
					{*/
					if (!memcmp(nums->code, code2, 16))
					{
						nums->scen = b;
						break;
					}
				}
				else
				{
					return(0);
				}
			}
			else
			{
				return(0);
			}
			fclose(input);
		}
	}
	if (b == 10)
	{
		bigmessage("The scenario file^for this saved game^cannot be found.");
		etc();
		return(-1);
	}
	return(1);
}

char getempire(struct PLAYER *plyr, struct UNITTYPE *info, struct TECH *tech, struct SORTS *sorts, struct NUMS *nums, struct MAP *map, char *scenname)
{
	// r is the number of researched technologies
	char b, e, f, s[26], s2[7];
	unsigned char c, h, j, l, r;
	// pi means power index
	long int pi[2], curgold;
	short *numunits, d, g, i, k, n, inc[2], incmu[2];
	float m;
	struct PLAYER *curplyr, *enemy, *newplyr;
	struct UNIT *unit;
	enemy = plyr + !turn;
	calculatepi(pi, plyr, info);
	for(k = 0; k <= 1; k++)
	{
		pi[k] = min(pi[k], 999999);
	}
	for(k = 0; k <= 1; k++)
	{
		inc[k] = income(curplyr = plyr + k, info, nums, map, sorts, &numunits); //+ incminingunits(plyr + k, info, map);
		tryfree(numunits);
		incmu[k] = 0;
		unit = curplyr->unit;
		for(d = 0; d < curplyr->units; d++)
		{
			if (unit->orders == mineforgold)
			{
				g = info[unit->uindex].mines;
				if (getmine(map, mapindex(unit->x, unit->y, map), unit->x % 2))
				{
					g <<= 1;
				}
				incmu[k] += g;
			}
			unit++;
		}
		inc[k] += incmu[k];
	}
	curplyr = plyr + turn;
	for(k = r = b = 0; k < nums->techs; k++)
	{
		if (curplyr->researched[k])
		{
			r++;
		}
	}
	curgold = (long int)curplyr->gold + (long int)nums->goldoverflow;
	do
	{
		GrayOff();
		ClrScr();
		DrawLineXor(0, 13, 159, 13);
		DrawLineXor(0, 57, 159, 57);
		DrawLine(80, 22, 80, 99, A_NORMAL);
		rectoutline(52, 13, 95, 21, A_NORMAL);
		/*DrawLine(52, 21, 95, 21, A_NORMAL);
		DrawLine(52, 14, 52, 20, A_NORMAL);
		DrawLine(95, 14, 95, 20, A_NORMAL);*/
		FontSetSys(F_8x10);
		DrawStrXor(2, 2, "END");
		DrawStrXor(29, 2, "TURN");
		DrawStrXor(70, 2, "EXIT");
		setfont6x8();
		DrawStrXor1(15, "Military");
		DrawStrXor(98, 15, "Technology");
		DrawStrXor(19, 59, "Economy");
		DrawStrXor(96, 59, "Settings");
		setfont4x6();
		/*DrawStr(103, 1, "civ89.tripod.com", A_NORMAL);
		DrawStr(116, 7, "/index.html", A_NORMAL);*/
		DrawStrXor(109, 1, "geocities.com/");
		DrawStrXor(119, 7, "civ89game/");
		if (turn)
		{
			//strcpy(s, "Gray's turn");
			DrawStrXor(54, 15, "Gray's turn");
		}
		else
		{
			//strcpy(s, "Black's turn");
			DrawStrXor(53, 15, "Black's turn");
		}
		//strcat(s, " turn");
		//DrawStrXor(53 + turn, 15, s);
		sprintf(s, "Units: %d (You) vs %d", curplyr->units, enemy->units);
		centerescreenl(s, 24);
		DrawStrXor(17, 34, "Power Index:");
		*s = 0;
		comma(s, pi[turn]);
		strcat(s, " (You) vs ");
		comma(s, pi[!turn]);
		if (DrawStrWidth(s, F_4x6) > 78)
		{
			*s = 0;
			comma(s, pi[turn]);
			strcat(s, "(You)vs");
			comma(s, pi[!turn]);
		}
		centerescreenl(s, 40);
		sprintf(s, "Cities: %d (You) vs %d", curplyr->cities, enemy->cities);
		centerescreenl(s, 50);
		centerescreenr("Researching:", 23);
		if ((c = curplyr->researching) != 255)
		{
			d = 8000 + 2000 * tech[c].age;
			m = (float)curplyr->researchp40 / (float)d;
			sprintf(s, "%s-%d%%", tech[c].id, (short)(100.0 * m));
			drawrect(83, 30, (unsigned char)(83.5 + 73.0 * m), 36);
			rectoutline(83, 29, 157, 37, A_NORMAL);
			//DrawLine(83, 30, 83, 38, A_NORMAL);
			DrawStrXor(83, 31, s);
			//(short)ceil(40.0 * (float)d / (float)science(curplyr) * (1.0 - 0.025 * (float)curplyr->researchp40 / (float)d));
			g = (short)ceil((float)(d - curplyr->researchp40) / (float)science(curplyr));
			sprintf(s, "Advance in %d turn", g);
			if (g != 1)
			{
				strcat(s, "s");
			}
			centerescreenr(s, 39);
		}
		else
		{
			centerescreenr("(nothing)", 29);
		}
		sprintf(s, "Researched %d/%d", r, nums->techs);
		centerescreenr(s, 45);
		centerescreenr("Technologies", 51);
		sprintf(s, "Total Gold: %ld", curgold);
		centerescreenl(s, 70);
		sprintf(s + 6, "Income: %d", inc[turn]);
		centerescreenl(s, 76);
		sprintf(s, "Enemy Gold: %d", enemy->gold);
		centerescreenl(s, 82);
		sprintf(s + 6, "Income: %d", inc[!turn]);
		centerescreenl(s, 88);
		sprintf(s, "%d free units", 25 + 5 * curplyr->cities);
		centerescreenl(s, 94);
		sprintf(s, "Handicap: %d", plyr->handicap);
		if (curplyr->handicap > 0)
		{
			strcat(s, " (easy)");
		}
		else if (curplyr->handicap)
		{
			strcat(s, " (hard)");
		}
		centerescreenr(s, 69);
		sprintf(s, "Black: %s", *(nums->player));
		centerescreenr(s, 78);
		sprintf(s, "Gray: %s", nums->player[1]);
		centerescreenr(s, 84);
		sprintf(s, "Save File: %d", nums->game);
		centerescreenr(s, 93);
		//DrawStrXor(141, 94, "v0.18");
		highlight(b);
		k = nograyngetchx();
		do
		{
			c = b;
			if ((e = dirkeys2(k)) == 1)
			{
				if (b % 2)
				{
					b--;
				}
			}
			else if (e == 3)
			{
				if (!(b % 2))
				{
					b++;
				}
			}
			else if (e == 2)
			{
				if (b >= 2)
				{
					b -= 2;
				}
			}
			else if (e)
			{
				if (b <= 3)
				{
					b += 2;
				}
			}
			else if (k == 13)
			{
				switch(b)
				{
					case 0:
						return(126);
					case 1:
						//return(127);
						if (nums->autosave)
						{
							return(127);
						}
						if (reallyabandon())
						{
							return(127);
						}
						k = 0;
						break;
					case 2:
						// Military
						// Bar width = 136
						e = turn;
						/*FontSetSys(F_8x10);
						ClrScr();
						DrawStrXor(45, 2, "Unit Graph");*/
						bigmessage("Unit Graph");
						setfont6x8();
						/*DrawStrXor(69, 21, "You:");
						DrawStrXor(65, 56, "Enemy:");*/
						centertext("You:", 15);
						centertext("Enemy:", 54);
						setfont4x6();
						for(c = 30; c <= 69; c += 39)
						{
							constructnumunits(newplyr = plyr + e, &numunits, nums);
							rectoutline(2, c, 137, c + 11, A_NORMAL);
							d = 0;
							g = 501;
							j = 2;
							do
							{
								i = 0;
								for(h = 0; h < nums->infos; h++)
								{
									n = numunits[h];
									if (n > i && (n < g || (n == g && h > f)))
									{
										i = n;
										l = h;
									}
								}
								d += i;
								h = (unsigned char)(135.999 * (float)d / (float)newplyr->units) + 2 - j;
								if (h > 8)
								{
									Sprite8(j + ((h - 6) >> 1), c + 2, 8, sprites + ((l + 6) << 3), LCD_MEM, SPRT_OR);
									sprintfd(s, i);
									DrawStrXor(j + ((h - strlen(s) * 3) >> 1), c - 6, s);
									j += h;
									// DrawLineXor is not used because this would erase the rightmost border of the
									// rectangle if the "Other" category is empty
									DrawLine(j, c + 1, j, c + 10, A_NORMAL);
								}
								f = l;
								g = i;
							}
							while (h > 8);
							if (i)
							{
								// h is 1 more than the difference between the x coordinates of the vertical lines
								h = 137 - j;
								if (h > 18)
								{
									l = j + ((h - 18) >> 1);
								}
								else
								{
									l = 139;
								}
								DrawStrXor(l, c + 3, "Other");
								sprintfd(s, newplyr->units - d + i);
								DrawStrXor(j + max(((h - strlen(s) * 3) >> 1), 2), c - 6, s);
							}
							tryfree(numunits);
							sprintf(s, "%d unit", newplyr->units);
							if (newplyr->units != 1)
							{
								strcat(s, "s");
							}
							sprintf(s + strlen(s), " (%d free)", 25 + 5 * newplyr->cities);
							centertext(s, c + 13);
							e = !e;
						}
						etc();
						break;
					case 3:
						// Technology
						gettechnology(curplyr, tech, info, nums, sorts, 1);
						break;
					case 4:
						// Economy
						sprintf(s, "Total Gold: %ld", curgold);
						d = cityincome(curplyr->cities, curplyr->gpt);
						g = incminingcities(curplyr, map);
						//i = incminingunits(curplyr, info, map);
						setfont6x8();
						ClrScr();
						DrawStrXor1(1, s);
						DrawStrXor1(12, "City Revenues:");
						sprintf(s, "Total: %d", d);
						DrawStrXor1(44, s);
						// Note that cities can mine no more than 800 gold total
						sprintf(s, "Mining Cities: %d gold", g);
						DrawStrXor1(55, s);
						sprintf(s + 7, "Units: %d", incmu[turn]);
						strcat(s, " gold");
						DrawStrXor1(64, s);
						sprintf(s, "Unit Fees: %d", inc[turn] - (d + g + incmu[turn]));
						strcat(s, " gold");
						DrawStrXor1(73, s);
						sprintf(s, "Total Income: %d gold", inc[turn]);
						DrawStrXor1(82, s);
						setfont4x6();
						// x is c, y is h
						c = 8;
						h = 20;
						d = curplyr->gpt;
						e = 10;
						strcpy(s, "First 10:");
						for(f = 0; f <= 45; f += e)
						{
							DrawStrXor(c, h, s);
							sprintf(s2, "%dx%d", min(max(curplyr->cities - f, 0), 5 * (f <= 10) + 5), d);
							DrawStrXor(c + 2, h + 6, s2);
							if (!f)
							{
								strcpy(s, "Next 10:");
								d = 0;
							}
							else
							{
								d -= 10;
								if (f == 20)
								{
									e = 5;
								}
								else if (f == 10)
								{
									strcpy(s + 5, "5:");
								}
							}
							if (f != 25)
							{
								c += 40;
							}
							else
							{
								c = 8;
								h = 32;
							}
						}
						etc();
						break;
					// case 5:
					default:
						// Settings
						c = 18;
						// e is the game number, f is whether Autosave is on
						do
						{
							sprintf(s, "%s vs %s", *(nums->player), nums->player[1]);
							setfont6x8();
							ClrScr();
							DrawStrXor1(2, s);
							turnstr(s, nums);
							DrawStrXor1(11, s);
							sprintf(s, "Scenario %d:", nums->scen);
							DrawStrXor1(24, s);
							DrawStrXor1(33, scenname);
							sprintf(s, "Map %d: %s", nums->map, map->name);
							DrawStrXor1(46, s);
							sprintf(s, "Save File: %d", e = nums->game);
							emptyfull(e, 55);
							DrawStrXor1(55, s);
							strcpy(s, "Autosave Off");
							if ((f = nums->autosave))
							{
								//strcat(s, "n");
								strcpy(s + 10, "n");
							}
							/*else
							{
								strcat(s, "ff");
							}*/
							DrawStrXor1(64, s);
							centertext("Done", 74);
							centertext("Save Game", 83);
							centertext("New Game / Load Game", 92);
							highlight(c);
							do
							{
								d = c;
								k = nograyngetchx();
								if ((h = dirkeys2(k)) == 2)
								{
									if (c > 16)
									{
										c--;
									}
								}
								else if (h == 4)
								{
									if (c < 20)
									{
										c++;
									}
								}
								else if (h || k == '+' || k == '-' || isdigit(k))
								{
									if (c == 16)
									{
										j = e;
										if (isdigit(k))
										{
											e = k - '0';
										}
										else
										{
											if (h == 3 || k == '+')
											{
												e++;
											}
											else
											{
												e += 9;
											}
											e %= 10;
										}
										if (j != e)
										{
											DrawCharXor(67, 55, j + '0');
											DrawCharXor(67, 55, e + '0');
											if (e == nums->game || j == nums->game)
											{
												DrawLineXor(76, 58, 79, 58);
											}
											emptyfull(e, 55);
										}
									}
									else if (c == 17)
									{
										f = !f;
										DrawCharXor(61, 64, 'n');
										DrawStrXor(61, 64, "ff");
										DrawLineXor(76, 67, 79, 67);
									}
								}
								else if (k == 13)
								{
									if (c == 19)
									{
										nums->game = e;
										nums->autosave = f;
										if (!savegame(plyr, map, nums, scenname))
										{
											error(1);
											return(127);
										}
										DrawStr(79, 55, "(full) ", A_REPLACE);
										DrawLine(76, 58, 79, 58, A_REVERSE);
										DrawLine(76, 67, 79, 67, A_REVERSE);
										c = 18;
										k = 0;
									}
									else if (c == 20)
									{
										if (reallyabandon())
										{
											return(40);
										}
										k = -1;
									}
								}
								if (c != d)
								{
									highlight(d);
									highlight(c);
								}
								d = (k != 264 && (k != 13 || c != 18));
							}
							while (d && k != -1);
						}
						while (d);
						if (k == 13)
						{
							nums->game = e;
							nums->autosave = f;
						}
						b = 1;
					/*default:
						savegame(0, plyr, turn, map, infos, code, techs, turnnum, *goldoverflow, nums, scenname, maxturn);
						k = 0;*/
				};
				k = 0;
			}
			if (k && k != 264)
			{
				if (c != b)
				{
					highlight(c);
					highlight(b);
				}
				k = nograyngetchx();
			}
		}
		while (k && k != 264);
	}
	while (k != 264);
	return(32);
}

/*char unitsatspace(struct PLAYER *curplyr, char x, char y)
{
	short d;
	char e;
	e = 0;
	for(d = 0; d < curplyr->units; d++)
	{
		if (curplyr->unit[d].x == x && curplyr->unit[d].y == y)
		{
			e++;
		}
	}
	return(e);
}*/

char rest(struct UNIT *unit, struct UNITTYPE *curinfo)
{
	unsigned short d;
	if (unit->m2)
	{
		d = (unsigned short)curinfo->d * 30;
		// 0.075 = 15% / 2
		unit->hp10 = min(unit->hp10 + (unsigned short)(0.075 * (float)d * (float)unit->m2 / (float)curinfo->m), d);
		unit->m2 = 0;
		return(1);
	}
	else
	{
		return(0);
	}
}

// performs all pre-turn operations
static inline void pre(struct PLAYER *plyr, struct TECH *tech, struct UNITTYPE *info, struct MAP *map, struct SORTS *sorts, struct NUMS *nums, char *scenname)
{
	short a, d, e, f, g, j, center[25];
	unsigned char b, c, i;
	struct PLAYER *curplyr;
	struct TECH *curtech;
	//city25[index] is whether the city of id index needs to delay production
	//due to the 25-unit limit
	char s[112], s2[6];
	struct CITY *city;
	struct UNIT *unit, *unit2;
	short *numunits;
	curplyr = plyr + turn;
	GrayOff();
	if (turn)
	{
		strcpy(s, "Black");
		strcpy(s2, "gray");
	}
	else
	{
		strcpy(s, "Gray");
		strcpy(s2, "black");
	}
	sprintf(s + strlen(s), " has ended his^turn. It is now^%s's turn.", s2);
	bigmessage(s);
	/*strcpy(s, "Turn ");
	comma(s, turnnum);
	if (maxturn)
	{
		strcat(s, " / ");
		comma(s, maxturn);
	}*/
	turnstr(s, nums);
	centertext(s, 60);
	etc();
	a = curplyr->gold;
	nums->goldoverflow = 0;
	if (nums->turnnum > 1)
	{
		// Note that costs for units and cities will not exceed 16025
		a += income(curplyr, info, nums, map, sorts, &numunits);
		if (a < 0)
		{
			b = g = 0;
			if (numunits)
			{
				j = curplyr->units - (25 + 5 * curplyr->cities);
				for(c = 0; a < 0 && c < curplyr->units && g <= j; c++)
				{
					g += (e = numunits[i = sorts->csort[c]]);
					f = info[i].cost10;
					a += e * f;
				}
				if (--c < curplyr->units)
				{
					e -= (d = max(a / f, g - j));
					a -= d * f;
					g -= d;
					for(d = 0; d <= c; d++)
					{
						for(f = 0; f < curplyr->units; f++)
						{
							if (curplyr->unit[f].uindex == sorts->csort[d])
							{
								// It's impossible for this line to end the game
								delunit(curplyr, f);
								if (d == c)
								{
									if (!(--e))
									{
										break;
									}
								}
							}
						}
					}
					/*for(f = 0; d; f++)
					{
						if (curplyr->unit[f].uindex == sorts->csort[e])
						{
							// It's impossible for this line to end the game
							delunit(curplyr, f);
							d--;
						}
					}*/
				}
			}
			a -= cityincome(d = curplyr->cities, curplyr->gpt);
			while (/*This must be true: curplyr->cities > 20 + b &&*/ (e = a + cityincome(d, curplyr->gpt)) < 0)
			{
				// It's impossible for this line to end the game, as it will not delete a city beyond the 10th
				delcity(curplyr, --d);
				b++;
			}
			a = e;
			strcpy(s, "Due to a gold^shortage, you are^unable to pay the^");
			if (g)
			{
				sprintf(s + strlen(s), "revenues of %d^unit", g);
				if (g > 1)
				{
					strcat(s, "s");
				}
				if (b)
				{
					strcat(s, " and the^");
				}
				/*else
				{
					strcat(s, ".");
				}*/
			}
			if (b)
			{
				sprintf(s + strlen(s), "maintenance fees^for %d cit", b);
				if (b > 1)
				{
					strcat(s, "ies");
				}
				else
				{
					strcat(s, "y");
				}
			}
			strcat(s, ".");
			bigmessage(s);
			etc();
		}
		tryfree(numunits);
	}
	c = f = 0;
	city = curplyr->city;
	for(b = 0; b < curplyr->cities; b++)
	{
		if (city->building < 254)
		{
			//i = getassist5(curplyr, info, city->x, city->y);
			if ((city->percent3200 += ((unsigned short)(80.0 * (float)(20 + curplyr->pbonus5) * (float)(20 + getassist5(curplyr, info, city->x, city->y)) / (float)info[city->building].turns10)) + 1) >= 3200)
			{
				if (curplyr->units < 300)
				{
					e = 25;
					for(d = 0; d < curplyr->units && e; d++)
					{
						unit = curplyr->unit + d;
						if (unit->x == city->x && unit->y == city->y)
						{
							e--;
						}
					}
					//if (unitsatspace(curplyr, city->x, city->y) != 25)
					if (e)
					{
						//curplyr->unit = (struct UNIT *)realloc_throw(curplyr->unit, ++curplyr->units * sizeof(struct UNIT));
						unit = curplyr->unit + curplyr->units++;
						unit->hp10 = info[unit->uindex = city->building].d * 30;
						unit->tindex = 511;
						unit->x = city->x;
						unit->y = city->y;
						unit->orders = unit->percent2 = 0;
						city->percent3200 = 0;
					}
					else
					{
						f++;
						g = b + 1;
						city->percent3200 = 3199;
					}
				}
				else
				{
					c++;
					city->percent3200 = 3199;
				}
			}
			/*debugfloat(1.0 + (float)plyr[turn].pbonus5 * 0.05);
			debugfloat(10.0 + (float)(city->assist5) * 0.5);
			debugfloat((float)info[city->building].turns10);
			debugfloat((1.0 + (float)plyr[turn].pbonus5 * 0.05) * (10.0 + (float)(city->assist5) * 0.5) / (float)info[city->building].turns10);
			debugfloat(city->percent);*/
		}
		city++;
	}
	if (c)
	{
		sprintf(s, "You have reached^the unit limit of^300. %d additional^unit", c);
		if (c != 1)
		{
			strcat(s, "s were");
		}
		else
		{
			strcat(s, " was");
		}
		strcat(s, "^destroyed.");
		bigmessage(s);
		etc();
	}
	else if (f)
	{
		if (f == 1)
		{
			sprintf(s, "City %d", g);
		}
		else
		{
			sprintf(s, "%d cities", f);
		}
		strcat(s, " reached^the unit limit of^25. Unit production^is delayed.");
		//strcat(s, "delayed.");
		/*strcat(s, "the unit^limit of 25. Unit^production is^delayed.");
		if (f == 1)
		{
			g = (g > 9);
			s[27 + g] = s[45 + g] = s[59 + g] = ' ';
			s[36 + g] = s[56 + g] = '^';
		}*/
		bigmessage(s);
		etc();
	}
	unit = curplyr->unit;
	for(b = 0; b < curplyr->units; b++)
	{
		unit->attacked = 0;
		unit->m2 = info[unit->uindex].m << 1;
		unit++;
	}
	nums->goldoverflow += (e = incminingunits(curplyr, info, map));
	updategold(&a, nums);
	curplyr->gold = a;
	d = -(income(curplyr, info, nums, map, sorts, &numunits) + e);
	tryfree(numunits);
	if (3 * d > a)
	{
		sprintf(s, "You are running low^on gold! If you do^not raise more^gold, you will^begin losing units^and cities in %d^turns.", a / d + 1);
		if (a < d)
		{
			strcpy(s + 108, ".");
		}
		bigmessage(s);
		etc();
	}
	b = 0;
	unit = curplyr->unit;
	for(a = 0; a < curplyr->units; a++)
	{
		if (is124(unit->orders))
		{
			g = unit->percent2;
			if (build(curplyr, unit, info, map, 0) == 2)
			{
				b = 1;
			}
			// This indicates that construction was completed
			else if (!unit->percent2)
			{
				// The function of this code is to ensure that movement points are consumed in the proper order,
				// that is, units with higher unit type indeces first, ties broken by higher unit index
				f = -1;
				i = unit->orders;
				for(d = 0; d < curplyr->units; d++)
				{
					unit2 = curplyr->unit + d;
					if (unit2->x == unit->x && unit2->y == unit->y && unit2->orders == i)
					{
						// Note that the following line was commented due to a faulty return value in construction2
						// later
						//unit2->orders = none;
						for(e = f++; e >= 0; e--)
						{
							if (curplyr->unit[center[e]].uindex <= unit2->uindex)
							{
								break;
							}
							center[e + 1] = center[e];
						}
						if ((center[e + 1] = d) < a)
						{
							// Changed to unit2
							g -= construction2(unit2, info);
						}
					}
				}
				/*for(c = 0; c <= f; c++)
				{
					if (center[c] < a)
					{
						g -= construction(curplyr->unit + center[c], info);
					}
				}*/
				// Changed occurrences of unit to unit2
				c = 0;
				do
				{
					g += (d = construction2(unit2 = curplyr->unit + center[c++], info));
					if (g < 200)
					{
						unit2->m2 = unit2->orders = 0;
					}
				}
				while (g < 200);
				// I chose to round to the nearest integer here in order to combat rounding problems in percent2
				unit2->m2 = (unsigned char)((float)info[unit2->uindex].m * ((float)((g << 1) - 400) / (float)d) + 0.5);
				for(c--; c <= f; c++)
				{
					curplyr->unit[center[c]].orders = none;
				}
			}
		}
		unit++;
	}
	if (b)
	{
		bigmessage("You have reached^the city limit of^50. City^construction^delayed.");
		etc();
	}
	b = ((c = curplyr->researching) == 254);
	if (c < 254)
	{
		curtech = tech + c;
		// Cheat: use this line to make advances take one turn
		//if ((curplyr->researchp40 += 10000) >= rcost40(tech[plyr[turn].researching].age))
		if ((curplyr->researchp40 += science(curplyr)) >= 8000 + 2000 * curtech->age)
		{
			sprintf(s, "You have developed^the technology of^%s.", curtech->id);
			bigmessage(s);
			etc();
			curplyr->pbonus5 = min(curplyr->pbonus5 + curtech->prod5, 200);
			curplyr->rbonus5 = min(curplyr->rbonus5 + curtech->sci5, 200);
			curplyr->gpt = min(curplyr->gpt + curtech->gold, 200);
			if (curtech->bridge)
			{
				curplyr->bridge = 1;
			}
			for(a = 0; a < nums->infos; a++)
			{
				if (info[a].tech == c)
				{
					curplyr->active[a] = 1;
				}
			}
			redoapos(curplyr, nums, sorts);
			curplyr->researched[c] = 1;
			b = 1;
		}
	}
	if (b)
	{
		gettechnology(curplyr, tech, info, nums, sorts, 0);
	}
	if (nums->autosave)
	{
		savegame(plyr, map, nums, scenname);
	}
}

// 93 is the upper limit of the argument to the ok function
void ok(unsigned char y)
{
	DrawStrXor100(y, "F1:OK");
	while (grayngetchx() != 268);
}

// 85 is the upper limit of the argument to the yn function
char yn(unsigned char y)
{
	DrawStrXor100(y, "F1:Yes");
	DrawStrXor100(y + 8, "F2:No");
	return(f1f2());
}

// attackmod is the modifier for attacking based on the blast radius; it is equal to 1 - (# of spaces from
// attack center, measured as the maximum of {delta x, delta y}) / (blast radius)
// Returns 1 to redraw, 0 else
static inline char attackspace(char x, char y, struct PLAYER *plyr, struct UNIT *unit, struct UNITTYPE *info, float attackmod)
{
	char a, g, h, i;
	short b, c, e;
	float d, f, hp;
	struct PLAYER *curplyr;
	struct UNIT *unit2;
	struct UNITTYPE *curinfo;
	/*if (x == 13 && y == 8)
	{
		char inf[30];
		sprintf(inf, "%f", attackmod);
		ClrScr();
		DrawStrXor(0, 0, inf);
		ngetchx();
		exit(0);
	}*/
	curinfo = info + unit->uindex;
	f = attackmod * (float)curinfo->a * (0.7 + 0.6 * (float)rand() / 32767.0) * (float)(plyr[turn].handicap + 10);
	if (curinfo->cau)
	{
		f *= 3.0;
	}
	g = 0;
	do
	{
		c = -1;
		// hp starts at -1 to account for units for which d = 0
		hp = -1.0;
		a = !turn;
		do
		{
			curplyr = plyr + a;
			for(b = 0; b < curplyr->units; b++)
			{
				unit2 = curplyr->unit + b;
				if (unit2->x == x && unit2->y == y && unit2->tindex == 511) //&& unit2 != unit)
				{
					//curinfo = info + unit2->uindex;
					e = info[unit2->uindex].type;
					if (e == air || e == bmr)
					{
						h = curinfo->vsa5;
					}
					else if (e == ha)
					{
						h = curinfo->vsha5;
					}
					else
					{
						h = 20;
					}
					if (h)
					{
						d = (float)unit2->hp10 * (float)h;
						if (d > hp)
						{
							hp = d;
							c = b;
							i = h;
						}
					}
				}
			}
			if (a == turn)
			{
				break;
			}
			else if (c == -1)
			{
				a = turn;
			}
		}
		while (c == -1);
		if (c == -1)
		{
			break;
		}
		/*if (c != -1)
		{*/
			//curplyr = plyr + a;
			unit2 = curplyr->unit + c;
			//curinfo = info + unit2->uindex;
			e = (short)(f * (float)i / 20.0 + 0.5);
			if (!e)
			{
				if (!i || attackmod != 1.0)
				{
					break;
				}
				else
				{
					e = 1;
				}
			}
			f -= (float)min((short)unit2->hp10, e);
			/*if (x == 13 && y == 8)
			{
				ClrScr();
				char inf[30];
				sprintf(inf, "%f", f);
				DrawStrXor1(0, inf);
				ngetchx();
			}*/
			/*unit->attacked = 1;
			unit->m2 -= 2;*/
			b = unit2->hp10 - e;
			if (b <= 0)
			{
				g = 1;
				// Code to destroy all units if stacked
				for(b = 0; b < curplyr->cities; b++)
				{
					if (curplyr->city[b].x == x && curplyr->city[b].y == y)
					{
						break;
					}
				}
				if (b < curplyr->cities)
				{
					if (info[unit2->uindex].trans)
					{
						for(b = 0; b < curplyr->units; b++)
						{
							if (curplyr->unit[b].tindex == c)
							{
								if (c > b)
								{
									c--;
								}
								delunit(curplyr, b--);
							}
						}
					}
					delunit(curplyr, c);
				}
				else
				{
					for(b = 0; b < curplyr->units; b++)
					{
						if (curplyr->unit[b].x == x && curplyr->unit[b].y == y)
						{
							delunit(curplyr, b--);
						}
					}
					break;
				}
			}
			//else if (g != 1)
			else
			{
				unit2->hp10 = b;
				if (!g)
				{
					//g = 0;
					a = unit2->uindex;
					unit2 = curplyr->unit;
					for(c = 0; c < curplyr->units; c++)
					{
						e = unit2->hp10 - b;
						if (unit2->x == x && unit2->y == y && unit2->tindex == 511 && (e > 0 || (!e && unit2->uindex < a)))
						{
							g = 1;
							break;
						}
						unit2++;
					}
				}
			}
		/*}
		else
		{
			return(g);
		}*/
	}
	while (f >= 0.5 && curinfo->cau);
	return(g);
}

// Returns 1 to redraw, 0 else
char attack(char x, char y, struct PLAYER *plyr, struct UNIT *unit, struct UNITTYPE *info, struct MAP *map)
{
	// attackspace is performed on the following x-coordinates: x1 to x2, 0 to x3
	char a, xc, yc;
	short br;
	struct UNITTYPE *curinfo;
	struct UNIT tempunit;
	memcpy(&tempunit, unit, sizeof(struct UNIT));
	unit->orders += 8;
	curinfo = info + unit->uindex;
	br = curinfo->br;
	a = 0;
	for(yc = max(y - br, 0); yc <= min(y + br, map->h); yc++)
	{
		for(xc = -br; xc <= br; xc++)
		{
			if (attackspace(adj(x + xc, map), yc, plyr, &tempunit, info, 1.0 - (float)max(abs(xc), abs(yc - y)) / (float)(br + 1)))
			{
				a = 1;
			}
			/*b = attackspace(adj(x + xc, map), yc, plyr, turn, unit, info, 1.0 - (float)max(xc, abs(yc - y)) / (float)(br + 1));
			if (b == 1)
			{
				a = 1;
			}
			else if (a == 2 && !b)
			{
				a = 0;
			}*/
		}
	}
	plyr += turn;
	unit = plyr->unit;
	for(br = 0; br < plyr->units; br++)
	{
		if (unit->orders >= 8)
		{
			if (curinfo->cau)
			{
				delunit(plyr, br);
			}
			else
			{
				unit->orders -= 8;
				unit->attacked = 1;
				unit->m2 -= 2;
			}
			break;
		}
		unit++;
	}
	return(a);
}

/*char attack(short x, short y, struct PLAYER *plyr, char turn, struct UNIT *unit, struct UNITTYPE *info, struct MAP *map)
{
	// attackspace is performed on the following x-coordinates: x1 to x2, 0 to x3
	char x1, x2, x3, y1, y2, xc, yc, a, b, br;
	br = info[unit->uindex].br;
	if (!br)
	{
		a = attackspace(x, y, plyr, turn, unit, info, 1.0);
	}
	else
	{
		a = 2;
		y1 = max(y - br, 0);
		y2 = min(y + br, map->h);
		x3 = -1;
		if ((br << 1) < map->w)
		{
			x1 = adj(x - br, map);
			x2 = adj(x + br, map);
			if (x1 > x2)
			{
				x3 = x2;
				x2 = map->w;
			}
		}
		else
		{
			x1 = 0;
			x2 = map->w;
		}
		for(yc = y1; yc <= y2; yc++)
		{
			for(xc = 0; xc <= x2; xc++)
			{
				if (xc > x3)
				{
					xc = x1;
				}
				b = attackspace(xc, yc, plyr, turn, unit, info, 1.0 - (float)max(min(abs(adj(xc - x, map)), abs(xc - x)), abs(yc - y)) / (float)(br + 1));
				if (b == 1)
				{
					a = 1;
				}
				else if (a == 2 && !b)
				{
					a = 0;
				}
			}
		}
	}
	if ((unit->attacked = (a != 2)))
	{
		unit->m2 -= 2;
	}
	return(a);
}*/

/*void redoassist5(struct PLAYER *plyr, char turn, short *center, struct UNITTYPE *info)
{
	unsigned char a, b;
	a = 0;
	for(a = 0; center[a] < 30000 && a <= 24; a++)
	{
		if (plyr[turn].unit[center[a]].orders == 1)
		{
			b = info[plyr[turn].unit[center[a]].uindex].assist5;
			if (b > a)
			{
				a = b;
			}
		}
	}
	plyr[turn].city[center[26]].assist5 = a;
}*/

// The stop function stops a unit from production or from assisting production; these are the only two
// conditions that will effect the presence of "Stop" in the F-key menu
void stop(struct PLAYER *curplyr, struct UNIT *unit, struct UNITTYPE *info, short *center, struct MAP *map, struct NUMS *nums)
{
	char a;
	struct UNIT *unit2;
	if (unit->orders == mineforgold)
	{
		curplyr->gold -= mines(unit, info, map);
		updategold(&(curplyr->gold), nums);
	}
	// 2, 3, and 5 (building a city, building a canal, and building roads) are the only other possibilities
	else
	{
		for(a = 0; center[a] != 30000; a++)
		{
			unit2 = curplyr->unit + center[a];
			if (unit2->percent2)
			{
				unit2->percent2 -= (unsigned char)construction2(unit, info);
			}
		}
	}
	unit->orders = none;
}

char dispdirections(char *s, short k, char onefits)
{
	char a;
	a = dirkeys2(k);
	if (!onefits)
	{
		strcat(s, "^one space^");
	}
	else
	{
		//strcat(s, "one^space");
		if (a != 4)
		{
			strcat(s, "one^space ");
		}
		else
		{
			strcat(s, "one^space^");
		}
	}
	if (a == 2)
	{
		strcat(s, "up?");
	}
	else if (a == 4)
	{
		strcat(s, "down?");
	}
	else
	{
		if (!onefits)
		{
			strcat(s, "to the^");
		}
		else
		{
			strcat(s, "to^the ");
		}
		if (a == 1)
		{
			strcat(s, "left?");
		}
		else
		{
			strcat(s, "right?");
		}
	}
	return(yn(tbarmessage(s)));
}

char reallyrelease(char b, char maxexceeded, short k)
{
	char s[76];
	strcpy(s, "Really^release^");
	if (maxexceeded)
	{
		sprintf(s + 15, "the^maximum^of %d^units^from this^transport^unit?", b);
	}
	else
	{
		if (b > 1)
		{
			sprintf(s + 15, "all %d^units^from", b);
		}
		else
		{
			strcat(s, "the unit^in");
		}
		strcat(s, " this^transport^unit");
		if (k)
		{
			strcat(s, " ");
			return(dispdirections(s, k, 1));
		}
		else
		{
			strcat(s, "?");
		}
	}
	return(yn(tbarmessage(s)));
}

// Draws the information for a single unit
// Returns 8 less than the highest available y-coordinate for toolbar drawing
char detailedtbar(struct PLAYER *plyr, char isenemy, short index, struct UNITTYPE *info, struct MAP *map)
{
	short a;
	char b, s[16];
	struct UNIT *unit;
	struct PLAYER *curplyr;
	curplyr = plyr + (turn ^ isenemy);
	unit = curplyr->unit + index;
	// center is not used here because there is no assurance that the calling function has access to center
	// data for the appropriate space
	info += unit->uindex;
	setfont4x6();
	cleartbar();
	setdarkplane();
	DrawStrXor100(1, info->id);
	Sprite8or(144, 9, sprites + ((unit->uindex + 6) << 3), DARK_PLANE);
	if (unit->attacked)
	{
		setlightplane();
		drawrect(142, 7, 153, 18);
		setdarkplane();
	}
	swordshield(101, 8, GrayGetPlane(DARK_PLANE));
	DrawStrXor(125, 16, "HP:");
	DrawStrXor(118, 30, "Moves:");
	DrawStrXor100(44, "Orders:");
	/* Actions: [A Assist] (defunct)
							B Build a city
							C build a Canal
							D Detonate
							M Mine
							R build Roads
							S Stop
							T board a Transport unit
							X eXit a transport unit
							E rElease all units from a transport unit */
	a = (unit->percent2) >> 1;
	switch(unit->orders)
	{
		case 0:
			strcpy(s, "none");
			break;
		case 1:
			sprintf(s, "city-%d%%", a);
			break;
		case 2:
			sprintf(s, "canal-%d%%", a);
			break;
		case 3:
			strcpy(s, "mine");
			break;
		case 4:
			sprintf(s, "roads-%d%%", a);
			break;
		default:
			strcpy(s, "boarded");
	};
	DrawStrXor(125 + ((strlen(s) <= 8) << 1), 44, s);
	if (info->trans)
	{
		b = 0;
		for(a = 0; a < curplyr->units; a++)
		{
			if (curplyr->unit[a].tindex == index)
			{
				b++;
			}
		}
		sprintf(s, "Transporting: %d", b);
		DrawStrXor100(50, s);
		b = 48;
	}
	else
	{
		b = 42;
	}
	if (unit->m2 < 20)
	{
		a = 106;
	}
	else
	{
		a = 112;
	}
	if (unit->m2 % 2)
	{
		DrawCharXor(a + 2, 36, '1');
		DrawCharXor(a + 5, 37, '/');
		DrawCharXor(a + 8, 38, '2');
		a += 14;
	}
	setfont6x8();
	sprintfd(s, info->a);
	DrawStrXor(104, 8, s);
	sprintfd(s, info->d);
	DrawStrXor(126, 8, s);
	bar(22, unit->hp10, info->d * 3);
	sprintfd(s, unit->m2 >> 1);
	DrawStrXor100(36, s);
	sprintf(s, "/%d", info->m);
	DrawStrXor(a, 36, s);
	spacedetails(plyr + turn, map, LIGHT_PLANE);
	return(b);
}

/*void ral1mp(const char *s)
{
	char s2[45];
	sprintf(s2, "%sing^requires^at least 1^movement^point.", s);
	ok(tbarmessage(s2));
}*/

// a must be < 10000
/*void notenoughgold(short a, short b)
{
	char s[10];
	setuptbarmessage();
	DrawStr(100, 1, "You only", A_NORMAL);
	if (a <= 9)
	{
		sprintf(s, "have %d of", a);
		DrawStr(100, 9, s, A_NORMAL);
		strcpy(s, "the ");
		comma(s, b);
		DrawStr(100, 17, s, A_NORMAL);
		DrawStr(100, 25, "gold", A_NORMAL);
	}
	else
	{
		sprintf(s, "have %d", a);
		DrawStr(100, 9, s, A_NORMAL);
		if (b <= 999)
		{
			sprintf(s, "of the %d", b);
			DrawStr(100, 17, s, A_NORMAL);
			DrawStr(100, 25, "gold", A_NORMAL);
		}
		else
		{
			DrawStr(100, 17, "of the", A_NORMAL);
			s[0] = 0;
			comma(s, b);
			strcat(s, " gold");
			DrawStr(100, 25, s, A_NORMAL);
		}
	}
	DrawStr(100, 33, "necessary", A_NORMAL);
	DrawStr(100, 49, "this unit.", A_NORMAL);
	ok(61);
}*/

/*void notenoughgold(short a, short b, const char *c)
{
	char s[67];
	sprintf(s, "You only^have %d", a);
	if (a <= 9)
	{
		strcat(s, " of^the ");
		comma(s, b);
		strcat(s, "^");
	}
	else
	{
		if (b <= 99)
		{
			sprintf(s + strlen(s), "^of the %d^", b);
		}
		else
		{
			strcat(s, "^of the^");
			comma(s, b);
			if (b <= 999)
			{
				strcat(s, " ");
			}
			else
			{
				strcat(s, "^");
			}
		}
	}
	sprintf(s + strlen(s), "gold^necessary^to b%s^this^unit.", c);
	ok(tbarmessage(s));
}*/

/*char reallypaygold(long int a, short b, const char *d, const char *e)
{
	char s[70], c[10];
	strncpy(c, e, 9);
	c[9] = 0;
	sprintf(s, "Really^b%s^%s^for ", d, c);
	comma(s, b);
	strcat(s, "^gold? You^currently^have");
	if (a <= 999)
	{
		strcat(s, " ");
	}
	else
	{
		strcat(s, "^");
	}
	comma(s, a);
	strcat(s, "^gold.");
	return(yn(tbarmessage(s)));
}*/

/*void thisunitwouldexceedlimit(const char *s)
{
	char s2[55];
	sprintf(s2, "B%sing^this unit^would^exceed^the unit^limit of^300.", s);
	ok(tbarmessage(s2));
}*/

// disptext: 0 -> nothing, 1 -> "Board which?", 2 -> "Drop which?"
char getunit(short *center, struct PLAYER *plyr, struct UNITTYPE *info, struct NUMS *nums, struct TECH *tech, unsigned char *disp, struct MAP *map, struct SORTS *sorts, char disptext)
{
	// start2 is the index of the absolute (not current) top item
	// viewmode: 0 = overview, 1 = units within a group (defunct), 2 = city construction
	/* prevselected: There are 4 flags.
	126 = Draw everything
	125 = Draw everything except city, do not invert city
	124 = Draw everything except city, invert city
	123 = Draw nothing, do not invert a second selection
	(0 - 24, 122) = Draw nothing, invert prevselected
	*/
	char b, d, flag, redrawtbar, selected, start, start2, prevselected, prevstart, end, center25div4, viewmode, s[70], s2[10];
	unsigned char e;
	short a, cost, k;
	float bonus;
	struct PLAYER *curplyr;
	struct CITY *curcity;
	viewmode = (disptext >> 3);
	disptext %= 8;
	curplyr = plyr + turn;
	if (center[26] != 30000)
	{
		curcity = curplyr->city + center[26];
	}
	else
	{
		curcity = NULL;
	}
	bonus = getbonus(plyr + (center25div4 = center[25] >> 2), info, map);
	k = 0;
	redrawtbar = 1;
	if (!viewmode)
	{
		if (!curcity || turn != center25div4)
		{
			if (*center == 30000)
			{
				return(31);
			}
			selected = 0;
		}
		else
		{
			selected = 122;
		}
		start = start2 = 0;
		//for(end = -1; end <= 23 && center[end + 1] != 30000; end++);
		if ((viewmode = (*center == 30000 && selected)))
		{
			prevselected = 125;
			moreturns(curplyr, center, info, bonus);
			curplyr->curcity = center[26];
		}
		else
		{
			if (!disptext)
			{
				prevselected = 123;
			}
			else
			{
				prevselected = 126;
			}
			if (center[1] == 30000 && !selected)
			{
				if (turn == center25div4)
				{
					return(0);
				}
				else
				{
					/* Change this line to
					// "detailedtbar(plyr[center[25] >> 2].unit + center[selected], info, sprites, 1);"
					// if the maximum number of players is ever increased from 2
					detailedtbar(plyr + !turn, plyr[!turn].unit + center[selected], info, map, sprites, 1);
					do
					{
						k = ngetchx();
					}
					while (k != 13 && k != 264);
					return(27);*/
					k = -1;
					redrawtbar = 0;
				}
			}
		}
	}
	else
	{
		prevselected = 126;
	}
	/*if (viewmode)
	{
		end = aposend(curplyr);
		start = start2 = selected = (curcity->building >= 254);
	}*/
	prevstart = start;
	do
	{
		flag = (k == 264 || k == 4361);
		if (redrawtbar || (flag && !disptext))
		{
			if (!viewmode || flag)
			{
				if (flag && redrawtbar)
				{
					cleartbar();
					prevselected = 126;
					selected = 127;
				}
				else if (viewmode)
				{
					prevselected = 125 + redrawtbar;
					if (redrawtbar)
					{
						cleartbar();
					}
					else
					{
						clearbelowcity();
					}
					selected = 127;
				}
				else
				{
					if (start != prevstart || (flag && start != start2))
					{
						if (!curcity && !disptext)
						{
							cleartbar();
						}
						else
						{
							clearbelowcity();
						}
						if (prevselected != 122 && selected != 122)
						{
							prevselected = 125;
						}
						else if (prevselected != 123 && prevselected != 125)
						{
							prevselected = 124;
						}
					}
					else if (prevselected == 126)
					{
						cleartbar();
					}
					if (flag)
					{
						if (selected == 122)
						{
							prevselected = 123;
						}
						else //if (start == start2)
						{
							selected = 127;
						}
						//start = 0;
					}
				}
				if (flag)
				{
					start = start2 = 0;
				}
				end = tbar(start, selected, prevselected, start != start2, center, plyr, info, map, bonus, disptext);
			}
			else
			{
				if (prevselected >= 124)
				{
					end = aposend(curplyr);
					start = start2 = selected = (curcity->building >= 254);
				}
				if (prevselected == 126)
				{
					cleartbar();
					a = 3;
				}
				else
				{
					if (prevselected == 124)
					{
						clearbelowcity();
					}
					else if (start != prevstart)
					{
						clearbelowcity();
						prevselected = 125;
					}
					a = 0;
				}
				
				cost = info[curcity->building].cost10 * 10;
				if (prevselected >= 122 && !(prevselected % 2))
				{
					//a = (prevselected == 126);
					drawcity(curcity, center, a);
					if (a)
					{
						spacedetails(curplyr, map, DARK_PLANE);
					}
					else if (prevselected == 124)
					{
						invertcity();
					}
				}
				// k is the starting y coordinate, e is the current y coordinate
				k = e = 22 + ((start != start2) << 3);
				setfont6x8();
				if (prevselected > 123)
				{
					//end -= 3;
					//if (start == 127)
					//{
					//	d = 0;
					//}
					//else
					//{
						//d = start - 3;
					//}
					if ((d = start - 3) > -1)
					{
						b = curplyr->apos[d];
					}
					else
					{
						b = 0;
					}
					setdarkplane();
					redrawtbar = 8;
					if (start != start2)
					{
						DrawCharXor(100, 22, UPCHAR);
						redrawtbar = 7;
					}
					if (redrawtbar + start < end)
					{
						DrawCharXor(100, 86, DOWNCHAR);
						redrawtbar--;
					}
					for(a = 0; a <= redrawtbar; a++)
					{
						if (d + 3 > end)
						{
							break;
						}
						setdarkplane();
						citystring(info + b, cost, d, e);
						
						//Now this is performed later
						//if (a + start == selected)
						//{
						//	GraySetAMSPlane(LIGHT_PLANE);
						//	DrawStr(100, y, s, A_NORMAL);
						//}
						e += 8;
						if (++d >= 0)
						{
							b = curplyr->apos[d];
						}
					}
				}
				else
				{
					drawcitybox(curplyr, info, cost, bonus, prevselected, start, k);
					setfont6x8();
				}
				drawcitybox(curplyr, info, cost, bonus, selected, start, k);
				//citytbar(start, end, selected, prevselected, start != start2, bonus, center, curplyr, info, sprites, info[curcity->building].cost20 * 20, map);
			}
			prevselected = selected;
			prevstart = start;
			redrawtbar = 0;
		}
		if (!flag)
		{
			if (k != -1)
			{
				k = grayngetchx();
			}
			else
			{
				k = 13;
			}
			b = dirkeys(k);
			if (b >= 4)
			{
				switch(b)
				{
					case 4:
						b = 2;
						break;
					case 5:
						b = 8;
						break;
					default:
						b = 127;
				};
				for(b--; b; b--)
				{
					if (selected != end)
					{
						if (selected != 122)
						{
							a = start + 8 + (!curcity && !disptext) - (start != start2);
							if (a < end)
							{
								a--;
							}
							/*if (a == 9)
							{
								invscreen();
								invscreen();
							}*/
							if (selected++ == a)
							{
								if (start++ == start2)
								{
									start++;
								}
							}
							/*if (selected == 8 && !start && a == 8)
							{
								exit(0);
							}*/
						}
						else // This is always true:  if (end >= 0)
						{
							selected = start;
						}
						/*else
						{
							break;
						}*/
					}
					else
					{
						break;
					}
					redrawtbar = 1;
				}
			}
			else if (b)
			{
				switch(b)
				{
					case 2:
						b = 7;
						break;
					// The following line must not read "default:"
					case 3:
						b = 127;
				};
				for(d = 1; d <= b; d++)
				{
					if (selected == 122)
					{
						break;
					}
					if (selected != /*start || start !=*/ start2)
					{
						if (selected-- == start)
						{
							if (--start == start2 + 1)
							{
								start--;
							}
						}
					}
					else if (/* This is always true:  end >= 0 &&*/ !viewmode && turn == center25div4 && curcity)
					{
						selected = 122;
					}
					else
					{
						break;
					}
					redrawtbar = 1;
				}
			}
			else if (k == 13)
			{
				redrawtbar = 1;
				if (selected == 122)
				{
					viewmode = 1;
					curplyr->curcity = center[26];
					/*end = aposend(curplyr);
					start = start2 = selected = (curcity->building >= 254);*/
					prevselected = 124;
					//debugchar(end);
				}
				else if (viewmode) //== 2)
				{
					// e cannot be assigned earlier because it is not known whether the space contains a city
					e = curcity->building;
					start = start2 = 0;
					if (selected >= 3)
					{
						if ((b = curplyr->apos[selected - 3]) != e)
						{
							curcity->building = b;
							curcity->percent3200 = 0;
						}
						else
						{
							redrawtbar = 0;
						}
					}
					else if (!selected)
					{
						if ((a = info[e].cost10 * 10) > curplyr->gold)
						{
							//notenoughgold(, a, "uy");
							sprintf(s, "You only^have %d", curplyr->gold);
							if (curplyr->gold <= 9)
							{
								sprintf(s + strlen(s), " of^the %d^", a);
							}
							else if (a <= 99)
							{
								sprintf(s + strlen(s), "^of the %d^", a);
							}
							else
							{
								sprintf(s + strlen(s), "^of the^%d ", a);
								/*if (a <= 9999)
								{
									strcat(s, " ");
								}
								else
								{
									strcat(s, "^");
								}*/
							}
							strcat(s, "gold^necessary^to buy^this^unit.");
							ok(tbarmessage(s));
						}
						else if (curplyr->units == 300)
						{
							//thisunitwouldexceedlimit("uy");
							ok(tbarmessage("Buying^this unit^would^exceed^the unit^limit of^300."));
						}
						else
						{
							strncpy(s2, info[e].id, 9);
							s2[9] = 0;
							sprintf(s, "Really^buy^%s^for %d^gold? You^currently^have", s2, a);
							// Note that curplyr->gold + nums->goldoverflow <= 999 and curplyr->gold <= 999 are
							// equivalent
							if (curplyr->gold <= 999)
							{
								strcat(s, " ");
							}
							else
							{
								strcat(s, "^");
							}
							sprintf(s + strlen(s), "%d^gold.", curplyr->gold + nums->goldoverflow);
							if (yn(tbarmessage(s)))
							{
								curplyr->gold -= a;
								updateminers(curplyr, info, map, nums);
								curcity->percent3200 = 3199;
							}
							else
							{
								return(27);
							}
						}
					}
					else
					{
						curcity->building = -selected;
						/*if (selected == 2)
						{
							curcity->building = 254;
						}
						else if (selected == 1)
						{
							curcity->building = 255;
						}*/
						curcity->percent3200 = 0;
					}
					k = 264;
				}
				else if (center25div4 != turn)
				{
					/*detailedtbar(plyr + !turn, plyr[!turn].unit + center[selected], info, map, sprites, 1);
					do
					{
						k = ngetchx();
					}
					while (k != 13 && k != 264);
					return(27);*/
					return(getaction(center[selected], plyr, info, nums, tech, disp, center, map, sorts, 1));
				}
				else
				{
					return(selected);
				}
			}
			//else if (((!viewmode && center[0] != 30000) || viewmode == 2) && (b = keyletter(keys, k)))
			else if (/* This is always true:  (*center != 30000 || viewmode) &&*/ (b = keyletter(k)))
			{
				/*if (viewmode == 2 && selected < 3)
				{
					selected = 3;
					c = 1;
				}
				else if (selected == 122)
				{
					selected = 0;
					c = 1;
				}*/
				selected = viewmode * 3;
				//a = 2;
				do
				{
					if (!viewmode)
					{
						e = plyr[center25div4].unit[center[selected]].uindex;
					}
					else
					{
						// In this context, plyr + center25div4 = curplyr
						e = curplyr->apos[selected - 3];
					}
					/*d = tolower(*(info[e].id));
					if (a == 2)
					{
						a = (d < b);
					}
					else if ((d < b) != a)
					{
						break;
					}
					if (a)
					{
						if (selected == end)
						{
							break;
						}
						selected++;
					}
					else
					{
						selected--;
					}*/
					if (selected++ == end)
					{
						break;
					}
				}
				while (tolower(*(info[e].id)) < b); //(++selected != viewmode * 3 - 1);
				/*if (selected == 1)
				{
					start = 0;
				}*/
				/*else if (selected == 3 && start2 && viewmode)
				{
					start = 1;
				}*/
				/*else
				{*/
					/*if (!a)
					{
						selected++;
					}*/
					start = end - (7 + (!curcity && !disptext));
					if (--selected < start)
					{
						start = selected;
					}
					/*else if (viewmode)
					{
						if (start < start2)
						{
							start = start2;
						}
					}*/
					if (start == 1)
					{
						start = 0;
					}
				//}
				start = max(start, start2);
				redrawtbar = (selected != prevselected || start != prevstart);
			}
			else if (k == 268 && viewmode && selected >= 3)
			{
				infotbar(curplyr, tech, info, nums, sorts, 0, curplyr->apos[selected - 3], 1);
				prevselected = 126;
				redrawtbar = 1;
			}
			else if (k == 266)
			{
				// Mode
				curplyr->showhp = !curplyr->showhp;
				if ((redrawtbar = !viewmode))
				{
					prevselected = 126 - (curcity || disptext);
					prevstart++;
				}
			}
			else if (selected == 122 || viewmode) //== 2)
			{
				if (k == 257)
				{
					// backspace
					prevselected = 126;
					if (/*unitsatspace(curplyr, adj(xcenter(curplyr), map), ycenter(curplyr))*/ disp[xy(curplyr->xo, curplyr->yo) + 121] >= 80)
					{
						ok(tbarmessage("You can't^demolish^a city^with more^than 10^units."));
					}
					else
					{
						for(a = 0; (b = (center[a] != 30000 && a <= 24)); a++)
						{
							if (info[curplyr->unit[center[a]].uindex].type == wat)
							{
								ok(tbarmessage("You must^disband^all water^units^present^before^you can^demolish^a city."));
								break;
							}
						}
						if (!b)
						{
							if (yn(tbarmessage("Really^demolish^this^city?")))
							{
								delcity(curplyr, center[26]);
								if (gameover(plyr))
								{
									return(30);
								}
								else
								{
									return(32);
								}
							}
						}
					}
					redrawtbar = 1;
				}
			}
		}
	}
	while (!flag);
	/*if (!start && !viewmode && !disptext)
	{
		if (selected == 122)
		{
			prevselected = 123;
		}
		else
		{
			prevselected = 125;
			selected = 127;
		}
		//drawbox(plyr + center25div4, info, center, selected, start, center[25] % 2, curplyr->showhp, 22 - ((center[26] == 30000) << 3));
		tbar(0, selected, prevselected, 0, center, plyr, turn, info, sprites, map, bonus, 0);
		return(31);
	}
	else
	{
		return(27);
	}*/
	if (!disptext)
	{
		return(31);
	}
	else
	{
		return(27);
	}
}

void invscreen()
{
	setdarkplane();
	drawrect0(0, 159, 99);
	setlightplane();
	drawrect0(0, 159, 99);
}

char dispthis(const char *s)
{
	setfont6x8();
	setdarkplane();
	DrawStrXor100(60, s);
	DrawStrXor100(68, "this?");
	return(yn(80));
}

char cantransport(struct PLAYER *curplyr, short tindex, struct UNIT *transportee, struct UNITTYPE *info, char x, char y)
{
	struct UNIT *transporter;
	struct UNITTYPE *newinfo;
	char transporteetype;
	short a;
	char b;
	
	transporter = curplyr->unit + tindex;
	newinfo = info + transporter->uindex;
	transporteetype = info[transportee->uindex].type;
	if (transporter->x == x && transporter->y == y && tindex != transportee->tindex &&
				(newinfo->type == bmr ? transporteetype == bom :
					(transporteetype != bom &&
						(transporteetype == gro ? newinfo->transgro :
						(transporteetype == wat ? newinfo->transwat :
						(transporteetype == air ? newinfo->transair :
						(transporteetype == ha ? newinfo->transha :
						newinfo->transbmr))))))) {
		b = newinfo->trans;
		for(a = 0; b && a < curplyr->units; a++)
		{
			if (curplyr->unit[a].tindex == tindex)
			{
				b--;
			}
		}
		return(b);
	}
	else
		return(0);
}

char getaction(short index, struct PLAYER *plyr, struct UNITTYPE *info, struct NUMS *nums, struct TECH *tech, unsigned char *disp, short *center, struct MAP *map, struct SORTS *sorts, char isenemy)
{
	// oi means orders index
	// destroyed indicates whether the current unit was destroyed from cau, but immediate exit was
	// undesirable due to the need for the shock effect
	char action[5], s[63], g, h, x, y, oldorders, index2, redraw, redrawtbar, reevaluateorders, oi, destroyed, unittype;
	short a, b, c, d, e, f, k, shock, tempcenter[27];
	struct CITY *city, *city2;
	struct UNIT *unit, *unit2, *curplyrunit;
	struct UNITTYPE *curinfo, *newinfo;
	struct PLAYER *curplyr, *enemy, *newplyr;
	curplyr = plyr + (turn ^ isenemy);
	enemy = plyr + (!turn ^ isenemy);
	newplyr = plyr + turn;
	if (!isenemy)
	{
		curplyr->curunit = index;
	}
	unit = (curplyrunit = curplyr->unit) + index;
	curinfo = info + unit->uindex;
	unittype = curinfo->type;
	shock = redraw = destroyed = 0;
	redrawtbar = reevaluateorders = 1;
	oldorders = unit->orders;
	g = (unittype == bom);
	h = (unittype == bmr);
	do
	{
		if (redraw)
		{
			//setdisp(disp, center, plyr, turn, info, map);
			draw(disp, center, info, map, plyr);
			// This code assumes that unit points to a valid unit, whether or nor the unit is meaningful
			// (i.e. index can be >= units)
			oldorders = unit->orders;
			redraw = 0;
		}
		if (shock)
		{
			invscreen();
			delay(2);
			invscreen();
			shock = 0;
		}
		if (gameover(plyr))
		{
			return(30);
		}
		if (destroyed)
		{
			return(32);
		}
		if (redrawtbar)
		{
			if (isenemy)
			{
				oi = 0;
			}
			else if (reevaluateorders)
			{
				// Note that it is impossible to exceed 5 commands
				oi = 0;
				// This is reached before x and y are used
				x = unit->x;
				y = unit->y;
				if (!curinfo->trans)
				{
					unit2 = curplyrunit;
					for(c = 0; c < curplyr->units; c++)
					{
						if (cantransport(curplyr, c, unit, info, x, y))
						{
							// board a Transport unit
							action[oi++] = 'T';
							break;
						}
						unit2++;
					}
				}
				if (curinfo->br)
				{
					// Detonate
					action[oi++] = 'D';
				}
				a = x % 2;
				e = mapindex(x, y, map);
				if (!unit->orders)
				{
					if (!g)
					{
						if (curinfo->canals /*&& center[26] == 30000*/ && !getriver(map, e, a))
						{
							// build a Canal
							action[oi++] = 'C';
						}
						if (curinfo->buildmult10 && center[26] == 30000)
						{
							// Note that this code may require revision if the map is changed from its cylindrical
							// topology
							for(c = max(curplyr->yo - 1, 0); c <= min(curplyr->yo + 1, 10); c++)
							{
								for(b = curplyr->xo - 1; b <= curplyr->xo + 1; b++)
								{
									if (disp[xy(b, c)] >= 254)
									{
										b = c = 30000;
									}
								}
							}
							if (c < 30000)
							{
								// Build a city
								action[oi++] = 'B';
							}
						}
						if (curinfo->trans /*The following must be true: && oi < 5*/ && !getwater(map, e, a))
						{
							for(c = 0; c < curplyr->units; c++)
							{
								if (curplyrunit[c].tindex == index)
								{
									c = 30000;
								}
							}
							if (c >= 30000)
							{
								// rElease all units from a transport unit
								action[oi++] = 'E';
							}
						}
						if (curinfo->mines)
						{
							// If the unit is a bomber, then E (Drop) alphabetically precedes M (Mine), else it doesn't
							if (action[oi - 1] != 'E' || h)
							{
								// Mine
								action[oi++] = 'M';
							}
							else
							{
								strcpy(s + oi++ - 1, "ME");
								/*action[oi - 1] = 'M';
								action[oi++] = 'E';*/
							}
						}
						if (curinfo->buildmult10 /*&& center[26] == 30000*/ && !getroad(map, e, a) && (!getriver(map, e, a) || curplyr->bridge))
						{
							// build Roads
							action[oi++] = 'R';
						}
					}
				}
				else if (unit->orders < 5)
				{
					// Stop
					action[oi++] = 'S';
				}
				else if (!getwater(map, e, a) && (!g || center[26] != 30000))
				{
					// eXit a transport unit
					action[oi++] = 'X';
				}
			}
			a = detailedtbar(plyr, isenemy, index, info, map);
			/*DrawStr(100, 82, "More info at", A_NORMAL);
			DrawStr(100, 88, "civ89.tripod.com", A_NORMAL);
			DrawStr(111, 94, "/index.html", A_NORMAL);*/
			setdarkplane();
			strcpy(s, "F0:");
			for(c = 0; c < oi; c++)
			{
				/*DrawStrXor100(a += 8, "F :");
				DrawCharXor(106, a, c + '1');*/
				sprintf(s, "F%c:", c + '1');
				//s[1]++;
				switch(action[c])
				{
					/*case 'A':
						DrawStr(118, a, "Assist", A_NORMAL);
						break;*/
					case 'B':
						strcat(s, "City");
						break;
					case 'C':
						strcat(s, "Canal");
						break;
					case 'D':
						strcat(s, "Detnate");
						break;
					case 'M':
						strcat(s, "Mine");
						break;
					case 'R':
						strcat(s, "Roads");
						break;
					case 'S':
						strcat(s, "Stop");
						break;
					case 'T':
						strcat(s, "Board");
						break;
					case 'X':
						strcat(s, "Exit");
						break;
					//case 'E':
					default:
						if (h)
						{
							strcat(s, "Drop");
						}
						else
						{
							strcat(s, "Releas");
						}
				};
				//DrawStrXor(118, a, s);
				DrawStrXor100(a += 8, s);
			}
			if (a <= 88)
			{
				setfont4x6();
				//DrawStrXor100(82, "Bugs/Suggestions:");
				DrawStrXor100(88, "geocities.com/");
				DrawStrXor(110, 94, "civ89game/");
			}
		}
		redrawtbar = 1;
		
		k = grayngetchx();
		if ((b = keyletter(k)) == 'n')
		{
			if (enemy->units)
			{
				a = enemy->curunit;
				b = nextunit(disp, center, map, plyr, info);
				// Note that index cannot be used instead of a because of cases when isenemy = 1
				if (enemy->curunit != a || (isenemy && b != -1))
				{
					return(b);
				}
			}
			redrawtbar = 0;
		}
		else if (b == 'c')
		{
			if (nextcity(newplyr, map))
			{
				return(29);
			}
			redrawtbar = isenemy = 0;
		}
		else if (b == 'h')
		{
			infotbar(curplyr, tech, info, nums, sorts, 0, unit->uindex, 1);
			k = 264;
		}
		else if (!isenemy)
		{
			if ((a = dirkeys2(k)))
			{
				// Move / Attack
				x = curplyr->xo;
				y = curplyr->yo;
				if (a == 1)
				{
					x--;
				}
				else if (a == 3)
				{
					x++;
				}
				// Note that this code may require revision if the scrolling system is changed
				else if (a == 2)
				{
					if (y)
					{
						y--;
					}
					else
					{
						k = 0;
					}
				}
				else if (y < 10)
				{
					y++;
				}
				else
				{
					k = 0;
				}
				if (k)
				{
					index2 = xy(x, y);
					x = adj(curplyr->xc + x, map);
					y += curplyr->yc;
					a = disp[index2];
					f = disp[index2 + 121];
					// The following line tests whether the action is aggressive
					if (((a < 253 || a == 255) && f % 8 != turn) || g)
					{
						/*if (curinfo->bribe)
						{
							if (unit->m2 < 2)
							{
								ral1mp("Brib");
							}
							// Note that a cannot equal 254
							else if (a == 255)
							{
								ok(tbarmessage("You can't^bribe a^unit that^is within^a city."));
							}
							else if (f >= 8)
							{
								ok(tbarmessage("You can't^bribe^multiple^units at^once."));
							}
							else
							{
								//newinfo = info + plyr[f % 8].unit[a].uindex;
								newinfo = info + plyr[!turn].unit[a].uindex;
								if (curplyr->gold < (c = newinfo->cost20 * 40))
								{
									// nums->goldoverflow must be 0 if this point is reached
									notenoughgold(curplyr->gold, c, "ribe");
								}
								else if (curplyr->units == 300)
								{
									thisunitwouldexceedlimit("ribe");
								}
								else
								{
									if (reallypaygold((long int)curplyr->gold + (long int)nums->goldoverflow, c, "ribe", newinfo->id))
									{
										if (is124(unit->orders))
										{
											stop(curplyr, unit, info, center, map, nums);
										}
										curplyr->gold -= c;
										updateminers(curplyr, info, map, nums);
										for(b = 0; b < enemy->units; b++)
										{
											unit2 = enemy->unit + b;
											if (unit2->x == x && unit2->y == y)
											{
												break;
											}
										}
										//curplyr->unit = (struct UNIT *)realloc_throw(curplyr->unit, ++curplyr->units * sizeof(struct UNIT));
										copyunit(curplyrunit + curplyr->units++, unit2);
										delunit(enemy, b);
										if (curinfo->cau)
										{
											delunit(curplyr, index);
											return(32);
										}
										unit->m2 -= 2;
										redraw = 1;
									}
								}
							}
						}
						else*/ if (!curinfo->a)
						{
							b = 0;
							if (h)
							{
								for(d = 0; center[d] != 30000 && d <= 24; d++)
								{
									if (curplyrunit[center[d]].tindex == index)
									{
										b++;
									}
								}
							}
							/*if (!b)
							{
								ok(tbarmessage("That unit^can't^attack^because^it has an^attack^rating of^0."));
							}
							else
							{*/
							if (b)
							{
								strcpy(s, "Really^drop ");
								if (b > 1)
								{
									sprintf(s + 12, "all^%d bombs^in ", b);
								}
								else
								{
									strcat(s, "the^bomb in^");
								}
								strcat(s, "this^bomber");
								if (dispdirections(s, k, 0))
								{
									// 30001 is a flag that tags the current unit.  If it is destroyed, tindex will not be
									// 30001 later on.
									unit->tindex = 510;
									for(d = 0; b; d++)
									{
										unit2 = curplyrunit + center[d];
										if (unit2->tindex == index)
										{
											if (attack(x, y, plyr, unit2, info, map))
											{
												redraw = 1;
											}
											/*if (info[unit2->uindex].cau)
											{
												delunit(curplyr, c);
											}*/
											b--;
										}
									}
									unit = curplyr->unit;
									for(index = 0; index < curplyr->units; index++)
									{
										if (unit->tindex == 510)
										{
											unit->tindex = 511;
											break;
										}
										unit++;
									}
									if (index == curplyr->units)
									{
										// The bomber was destroyed in the blast
										destroyed = 1;
									}
									shock = 1;
								}
							}
							else
							{
								redrawtbar = 0;
							}
						}
						else if (unit->attacked)
						{
							ok(tbarmessage("Each unit^can only^attack^once per^turn."));
						}
						else if (unit->tindex != 511 && !g)
						{
							ok(tbarmessage("Boarded^units^can't^attack."));
						}
						else
						{
							// The following code checks whether an attack is possible, i.e. whether vsa and vsha allow
							// an attack
							if (!(d = curinfo->br))
							{
								unit2 = enemy->unit;
								for(b = d = 0; b < enemy->units; b++)
								{
									c = info[unit2->uindex].type;
									if (unit2->x == x && unit2->y == y && (c != air || curinfo->vsa5) && (c != ha || curinfo->vsha5))
									{
										d = 1;
										break;
									}
									unit2++;
								}
							}
							if (d)
							{
								b = 0;
								if (g)
								{
									if (curinfo->cau)
									{
										strcpy(s, "Really^detonate^this bomb");
										b = dispdirections(s, k, 0);
									}
								}
								else if (unit->m2 < 2)
								{
									//ral1mp("Attack");
									ok(tbarmessage("Attacking^requires^at least 1^movement^point."));
								}
								else
								{
									b = 1;
								}
								if (b)
								{
									if (unit->orders && unit->orders <= 4)
									{
										stop(curplyr, unit, info, center, map, nums);
									}
									redraw = attack(x, y, plyr, unit, info, map);
									destroyed = curinfo->cau;
									/*if (curinfo->cau)
									{
										delunit(curplyr, index);
										destroyed = 1;
										//redrawtbar = c;
									}*/
									//redraw = (!b || destroyed);
									shock = redrawtbar = 1;
								}
							}
							else
							{
								redrawtbar = 0;
							}
						}
					}
					else
					{
						if (a == 254 && f % 8 != turn && (unittype != gro || !curinfo->a || curplyr->cities == 50))
						{
							if (curplyr->cities == 50)
							{
								ok(tbarmessage("Capturing^this city^would^exceed^the city^limit of^50."));
							}
							else
							{
								ok(tbarmessage("Only^ground^units^with a^nonzero^attack^rating^can^capture^an enemy^city."));
							}
							redrawtbar = 1;
							e = 29999;
						}
						else if (unittype == air || unittype == ha || h)
						{
							e = 2;
						}
						else
						{
							c = mapindex(x, y, map);
							b = x % 2;
							if (unittype == wat)
							{
								if (getwater(map, c, b) || a >= 254)
								{
									e = 2;
								}
								else if (getriver(map, c, b))
								{
									e = 3;
								}
								else
								{
									b = 0;
									for(c = 0; center[c] != 30000 && c <= 24; c++)
									{
										unit2 = curplyrunit + center[c];
										if (unit2->tindex == index && unit2->m2 >= 2)
										{
											b++;
										}
									}
									if (b)
									{
										/*c = 10;
										for(e = 0; e < curplyr->units; e++)
										{
											if (curplyr->unit[e].x == x && curplyr->unit[e].y == y)
											{
												c--;
											}
										}*/
										/*c = disp[xy(x, y) + 121];
										if (c < 253)
										{
											c = 10 - (c >> 3);
										}
										else
										{
											c = 10;
										}*/
										if ((c = 9 - (disp[xy(x, y) + 121] >> 3))) //unitsatspace(curplyr, x, y)))
										{
											if (reallyrelease(min(b, c), b > c, k))
											{
												for(d = 0; center[d] != 30000 && d <= 24 && c; d++)
												{
													unit2 = curplyrunit + center[d];
													if (unit2->tindex == index && unit2->m2 >= 2)
													{
														unit2->tindex = 511;
														unit2->orders = none;
														unit2->x = x;
														unit2->y = y;
														unit2->m2 -= 2;
														c--;
													}
												}
												e = 30002;
												//redraw = 1;
											}
											else
											{
												e = 29999;
											}
										}
										else
										{
											e = 30000;
										}
									}
									else
									{
										e = 30000;
									}
								}
							}
							else if (getwater(map, c, b))
							{
								if (!curinfo->trans && unit->m2 >= 2)
								{
									e = 0;
									for(d = 0; d < curplyr->units; d++)
									{
										if (cantransport(curplyr, d, unit, info, x, y))
										{
											addtocenter(curplyr, info, tempcenter, d, e++);
										}
									}
									if (e)
									{
										tempcenter[e] = tempcenter[26] = 30000;
										tempcenter[25] = (e > 4) + (turn << 2);
										if (e > 1)
										{
											d = getunit(tempcenter, plyr, info, nums, tech, disp, map, sorts, 1);
										}
										else
										{
											//d = getunit(&keys, tempcenter, plyr, turn, info, sprites, grouppos, &map, 2);
											detailedtbar(plyr, isenemy, *tempcenter, info, map);
											if (dispthis("Board"))
											{
												d = 0;
											}
											else
											{
												d = 25;
											}
										}
										if (d <= 24)
										{
											unit->tindex = tempcenter[d];
											unit->x = x;
											unit->y = y;
											unit->m2 -= 2;
											unit->orders = board;
											e = 30002;
										}
										else
										{
											redrawtbar = 1;
											e = 29999;
										}
									}
									else
									{
										e = 30000;
									}
								}
								else
								{
									e = 30000;
								}
							}
							else if ((getroad(map, c, b) || a >= 254) && (getroad(map, mapindex(unit->x, unit->y, map), unit->x % 2) || center[26] != 30000))
							{
								e = 1;
							}
							else
							{
								e = 2;
							}
						}
						// Cheat: Use this line to make all moves require no movement points
						//e = 0;
						if (unit->m2 >= e)
						{
							if (a != 253 && a != 254)
							{
								if (a == 255)
								{
									c = 25;
								}
								else
								{
									c = 10;
								}
								unit2 = curplyrunit;
								for(b = 0; b < curplyr->units; b++)
								{
									if ((unit2->x == x && unit2->y == y) || unit2->tindex == index)
									{
										if (!(--c))
										{
											k = 0;
											ok(tbarmessage("This^action^would^violate^the unit^limit (25^for^cities,^10 for^other^spaces)."));
											break;
										}
									}
									unit2++;
								}
							}
							if (k)
							{
								if (is124(unit->orders))
								{
									strcpy(s, "Interrupt^");
									switch(unit->orders)
									{
										case 1:
											strcat(s, "city");
											break;
										case 2:
											strcat(s, "canal");
											break;
										default:
											strcat(s, "roads");
									};
									strcat(s, "^produc-^tion and^move this^unit ");
									if (!dispdirections(s, k, 1)) //!yn(tbarmessage(s)))
									{
										k = 0;
									}
									else
									{
										stop(curplyr, unit, info, center, map, nums);
									}
								}
								else if (unit->orders == mineforgold)
								{
									stop(curplyr, unit, info, center, map, nums);
								}
								if (k)
								{
									unit->x = x;
									unit->y = y;
									unit->m2 -= e;
									e = 30001;
									unit2 = curplyrunit;
									for(b = 0; b < curplyr->units; b++)
									{
										if (unit2->tindex == index)
										{
											unit2->x = x;
											unit2->y = y;
										}
										unit2++;
									}
									if (a == 254 && f % 8 != turn)
									{
										city2 = enemy->city;
										for(b = 0; b < enemy->cities; b++)
										{
											if (city2->x == x && city2->y == y)
											{
												break;
											}
											city2++;
										}
										//curplyr->city = (struct CITY *)realloc_throw(curplyr->city, ++curplyr->cities * sizeof(struct CITY));
										memcpy(city = curplyr->city + curplyr->cities++, city2, sizeof(struct CITY));
										city->building = 255;
										city->percent3200 = 0;
										delcity(enemy, b);
									}
									if (unit->orders == board)
									{
										unit->orders = none;
										unit->tindex = 511;
									}
									redraw = 1;
								}
							}
						}
						else if (e != 29999)
						{
							redrawtbar = 0;
						}
						if (e > 30000)
						{
							centerscreen(curplyr, map, x, y);
							if (e == 30002)
							{
								return(32);
							}
						}
					}
				}
			}
			else if (k == 257)
			{
				// backspace
				if (yn(tbarmessage("Really^disband^this^unit?")))
				{
					delunit(curplyr, index);
					if (gameover(plyr))
					{
						return(30);
					}
					else
					{
						return(32);
					}
				}
			}
			else if (k >= 268 && k < 268 + oi)
			{
				reevaluateorders = 1;
				switch(action[k - 268])
				{
					case 'B':
						// Build a city
						if (curplyr->cities == 50)
						{
							if (!yn(tbarmessage("Really^build^beyond^the city^limit of^50?")))
							{
								break;
							}
						}
						unit->orders = buildcity;
						break;
					case 'C':
						// build a Canal
						unit->orders = buildcanal;
						//redraw = build(curplyr, unit, info, map, 1);
						break;
					case 'D':
						// Detonate
						if (curinfo->cau)
						{
							detailedtbar(plyr, isenemy, index, info, map);
							setfont6x8();
							setdarkplane();
							DrawStrXor100(54, "Really");
							DrawStrXor100(62, "detonate?");
							a = yn(74);
						}
						else
						{
							a = 1;
						}
						if (a)
						{
							redraw = attack(x, y, plyr, unit, info, map);
							destroyed = curinfo->cau;
							/*if (curinfo->cau)
							{
								delunit(curplyr, index);
								destroyed = redraw = 1;
								redrawtbar = 0;
							}*/
							shock = 1;
						}
						break;
					case 'M':
						// Mine
						unit->orders = mineforgold;
						curplyr->gold += mines(unit, info, map);
						updategold(&(curplyr->gold), nums);
						break;
					case 'R':
						// build Roads
						unit->orders = buildroads;
						//redraw = build(curplyr, unit, info, map, 1);
						break;
					case 'S':
						// Stop
						stop(curplyr, unit, info, center, map, nums); 
						break;
					case 'T':
						// board a Transport unit
						e = 0;
						for(d = center[a = 0]; d != 30000 && a <= 24; d = center[++a])
						{
							if (cantransport(curplyr, d, unit, info, x, y))
							{
								tempcenter[e++] = d;
							}
						}
						tempcenter[e] = tempcenter[26] = 30000;
						tempcenter[25] = (e > 4) + (turn << 2);
						if (e > 1)
						{
							a = getunit(tempcenter, plyr, info, nums, tech, disp, map, sorts, 1);
						}
						else
						{
							//a = getunit(&keys, tempcenter, plyr, turn, info, sprites, &map, 2);
							detailedtbar(plyr, isenemy, *tempcenter, info, map);
							if (dispthis("Board"))
							{
								a = 0;
							}
							else
							{
								a = 25;
							}
						}
						if (a <= 24)
						{
							unit->tindex = tempcenter[a];
							unit->orders = board;
							k = 264;
						}
						break;
					case 'X':
						// Exit
						unit->tindex = 511;
						unit->orders = none;
						break;
					//case 'E':
					default:
						// rElease all or drop one
						if (!h)
						{
							// Release all units from a transport unit
							b = 0;
							for(a = 0; center[a] != 30000 && a <= 24; a++)
							{
								if (curplyrunit[center[a]].tindex == index)
								{
									b++;
								}
							}
							if (reallyrelease(b, 0, 0))
							{
								for(a = 0; center[a] != 30000 && a <= 24; a++)
								{
									unit2 = curplyrunit + center[a];
									if (unit2->tindex == index)
									{
										unit2->tindex = 511;
										unit2->orders = none;
									}
								}
							}
							return(32);
						}
						else
						{
							b = 0;
							for(a = 0; center[a] != 30000 && a <= 24; a++)
							{
								if (curplyrunit[center[a]].tindex == index)
								{
									tempcenter[b++] = center[a];
								}
							}
							tempcenter[b] = tempcenter[26] = 30000;
							tempcenter[25] = (b > 4) + (turn << 2);
							if (b > 1)
							{
								a = getunit(tempcenter, plyr, info, nums, tech, disp, map, sorts, 2);
							}
							else
							{
								detailedtbar(plyr, isenemy, *tempcenter, info, map);
								if (dispthis("Drop"))
								{
									a = 0;
								}
								else
								{
									a = 25;
								}
							}
							if (a <= 24)
							{
								unit->tindex = 510;
								unit2 = (unit = curplyrunit) + tempcenter[a];
								redraw = attack(x, y, plyr, unit2, info, map);
								/*if (info[unit2->uindex].cau)
								{
									delunit(curplyr, tempcenter[a]);
									redraw = 1;
								}*/
								for(index = 0; index < curplyr->units; index++)
								{
									if (unit->tindex == 510)
									{
										unit->tindex = 511;
										break;
									}
									unit++;
								}
								if (index == curplyr->units)
								{
									// The bomber was destroyed in the blast
									destroyed = 1;
								}
								shock = 1;
							}
						}
				};
				if (is124(unit->orders))
				{
					if ((redraw = build(curplyr, unit, info, map, 1)) == 2)
					{
						redraw = 0;
						ok(tbarmessage("You have^reached^the city^limit of^50. City^construc-^tion^delayed."));
					}
				}
				/*
				This is performed in stop
				if (center[26] != 30000)
				{
					for(a = c = 0; center[a] != 30000 && a <= 24; a++)
					{
						if (plyr[turn].unit[center[a]].orders == 1)
						{
							b = info[plyr[turn].unit[center[a]].uindex].assist5;
							if (b > c)
							{
								c = b;
							}
						}
					}
					plyr[turn].city[center[26]].assist5 = c;
				}*/
			}
			else if (b == 'r' || b == 's')
			{
				redrawtbar = rest(unit, curinfo);
			}
			else
			{
				redrawtbar = 0;
			}
		}
		else
		{
			redrawtbar = 0;
		}
	}
	while (k != 264 && k != 13 && k != 4361);
	if (unit->orders != oldorders)
	{
		for(a = 0; center[a] != index; a++);
		while (a <= 23 && center[a + 1] != 30000)
		{
			center[a] = center[a + 1];
			a++;
		}
		addtocenter(curplyr, info, center, index, a);
		if (a < 24)
		{
			center[a + 1] = 30000;
		}
	}
	return(27);
}

// Returns -1 for no selection (meaning go back one screen), else the selection
char askwhichdatatoload(char datatype)
{
	char a, b[10], c, e, y, s[29], fname[14] = "main\\";
	short d;
	//struct NUMS nums;
	struct MAP map;
	FILE *input;
	ClrScr();
	setfont6x8();
	y = 2;
	c = -1;
	switch(datatype)
	{
		case 0:
			strcpy(fname + 5, "civgame0");
			break;
		case 1:
			strcpy(fname + 5, "scen0");
			break;
		default:
			strcpy(fname + 5, "map0");
	};
	for(a = 0; a <= 9; a++)
	{
		b[a] = 0;
		if ((input = fopen(fname, "rb")))
		{
			switch(datatype)
			{
				case 0:
					// Load game
					//if (fread(&d, 2, 1, input) && fread(&nums, sizeof(struct NUMS), 1, input) && fread(&e, 1, 1, input) && fread(s, 1, 11, input) == 11)
					if (!fseek(input, sizeof(struct NUMS) + 2, SEEK_SET))
					{
						if (!fread1(s, 11, input))
						{
							strcat(s, " vs ");
							b[a] = !fread1(s + strlen(s), 11, input);
						}
					}
					break;
				case 1:
					// Load scenario
					b[a] = !fread1(s, 21, input);
					break;
				default:
					// Load map
					if ((b[a] = fread(&map, sizeof(struct MAP), 1, input)))
					{
						strcpy(s, map.name);
					}
			};
			fclose(input);
			if (b[a])
			{
				DrawStrXor(13, y, s);
				DrawCharXor(1, y, a + '0');
				if (c == -1)
				{
					c = a;
				}
			}
		}
		fname[strlen(fname) - 1]++;
		y += 9;
	}
	/*if (!b[3])
	{
		exit(0);
	}*/
	if (c > -1)
	{
		setfont4x6();
		DrawStrXor1(94, "Use \x17\x18 to move, ENTER to select");
		setfont6x8();
		highlight(c + 32);
		do
		{
			a = c;
			d = nograyngetchx();
			e = dirkeys2(d);
			if (e)
			{
				if (e >= 3)
				{
					y = 1;
				}
				else if (e)
				{
					y = 9;
				}
				/*for(c = (c + y) % 10; c != a; c = (c + y) % 10)
				{
					if (b[c])
					{
						break;
					}
				}*/
				do
				{
					c = (c + y) % 10;
				}
				while (c != a && !b[c]);
			}
			if (a != c) //&& a >= 0 && a <= 9)
			{
				highlight(a + 32);
				highlight(c + 32);
			}
		}
		while (d != 13 && d != 264);
		if (d == 13)
		{
			return(c);
		}
		else
		{
			return(-1);
		}
	}
	else
	{
		strcpy(s, "There are no");
		if (!datatype)
		{
			strcat(s, " saved^games.");
		}
		else if (datatype == 1)
		{
			strcat(s, "^scenario files.");
		}
		else
		{
			strcat(s, " map^files.");
		}
		bigmessage(s);
		etc();
		return(-1);
	}
}

void startupxy(char *x, char *y)
{
	switch(*y)
	{
		case 0:
			*x = 73 + 6 * *x;
			*y = 2;
			break;
		case 1:
			*x = 91 + 6 * *x;
			*y = 19;
			break;
		case 2:
			*x = 61;
			*y = 28;
			break;
		case 3:
			*x = 67;
			*y = 45;
			break;
		case 4:
			*x = 43;
			*y = 65;
			break;
		case 5:
			*x = 37;
			*y = 74;
			break;
		default:
			*x = 68;
			*y = 85;
	};
}

void highlight2(char x, char y)
{
	char w;
	switch(y)
	{
		case 2:
			w = 12;
			break;
		case 4: case 5:
			w = 60;
			break;
		case 6:
			w = 24;
			break;
		default:
			w = 6;
	};
	startupxy(&x, &y);
	drawrect(x, y - 1, x + w, y + 7);
}

void drawhandicap(unsigned char *disp)
{
	char s[3];
	sprintfd(s, disp[8] - 5);
	DrawStrXor(61, 28, s);
}

short toint(unsigned char *s)
{
	return(1000 * *s + 100 * s[1] + 10 * s[2] + s[3]);
}

/*
startup flags:
40 title screen
41 load game
42 new game data
43 scenario #
44 starting units
45 map #
127 exit
*/

void cursor(char y, char x)
{
	if (x < 10)
	{
		x = (x - y) * 6 + 68;
		y = 9 * y + 36;
		DrawLineXor(x, y, x + 4, y);
	}
}

void _main(void)
{
	struct TECH *tech;
	struct PLAYER plyr[2], *curplyr;
	struct MAP map;
	struct UNITTYPE *info;
	struct NUMS nums;
	struct SORTS sorts;
	struct UNIT *unit;
	// xc means x corner, xo means xoffset (normal is 5)
	// disp flags: 253 = nothing, 254 = empty city, 255 = full city
	// disp indeces 121 to 241: 0 = black, 1 = gray, +8 = multiple units
	unsigned char /*sprites,*/ disp[242] = "=(),/|789*•456- 123t+0.xyz";
	//= {0xFFFFFFF0,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0xFFFFFFF0,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0xFFFFFFF0,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0xFFFFFFF0,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0xFFFFFFF0,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0xFFFFFFF0,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0xFFFFFFF0,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0xFFFFFFF0,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0xFFFFFFF0,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0xFFFFFFF0,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0xFFFFFFF0,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0x80402010,0xFFFFFFF0};
	long int pi[2];
	// turn indicates whose turn it is
	// grouppos (now defunct) indicated the position of each group (for displaying units)
	char s[42], b, c, d, e, f, g, h, i, j;
	// center[25]: 0 = units detailed, 1 = units undetailed, +4 = gray
	// The omission of 2 and 3 corresponds to the removal of display of groups of units of identical id
	// center[26] indicates the city number
	// The range for maxturn is 0 - 9999
	short a, k, center[27];
	
	if (OSFreeTimer(6))
	{
		if (OSRegisterTimer(6, 4000000000))
		{
			randomize();
			for(a = 0; a <= 25; a++)
			{
				let[a] = disp[a];
			}
			//strncpy(keys.let, disp, 26);
			let[15] = KEY_STO;
			
			a = 0;
			do
			{
				rects[a] = 0xFFFFFFF0;
				for(a++; a % 9 && a <= 98; a++)
				{
					rects[a] = 0x80402010;
				}
			}
			while (a <= 99);
			OSInitKeyInitDelay(250);
			OSInitBetweenKeyDelay(40);
			tech = NULL;
			info = NULL;
			sprites = NULL;
			sorts.csort = sorts.asort = /*sorts.tsort =*/ NULL;
			map.tile = NULL;
			//plyr = NULL;
			nums.player = (char **)malloc_throw(sizeof(char *) << 1);
			//plyr = (struct PLAYER *)malloc_throw(sizeof(struct PLAYER) << 1);
			curplyr = plyr;
			for(a = 0; a <= 1; a++)
			{
				nums.player[a] = (char *)malloc_throw(11);
				curplyr->active = curplyr->apos = curplyr->researched = NULL;
				curplyr->unit = NULL;
				curplyr->city = NULL;
				curplyr++;
			}
			NeedStack(7000);
			for(a = 0; a <= 1; a++)
			{
				if (!(plyr[a].unit = (struct UNIT *)malloc(300 * sizeof(struct UNIT))))
				{
					break;
				}
				else if (!(plyr[a].city = (struct CITY *)malloc(50 * sizeof(struct CITY))))
				{
					break;
				}
			}
			if (a == 2)
			{
				a = 40;
				/*while (kbhit())
				{
					ngetchx();
				}*/
				GKeyFlush();
				do
				{
					switch(a)
					{
						case 26:
							a = getempire(plyr, info, tech, &sorts, &nums, &map, s);
							break;
						case 126:
							if (turn)
							{
								if (++nums.turnnum > 30000)
								{
									nums.turnnum = 30000;
								}
								else if (nums.turnnum > nums.maxturn && nums.maxturn)
								{
									calculatepi(pi, plyr, info);
									GrayOff();
									setfont6x8();
									ClrScr();
									b = 13;
									curplyr = plyr;
									for(a = 0; a <= 1; a++)
									{
										strcpy(s, "Power Index: ");
										comma(s, pi[a]);
										centertext(s, b);
										sprintf(s, "Gold: %d", curplyr->gold);
										centertext(s, b + 9);
										strcpy(s, "2 x PI + Gold: ");
										comma(s, pi[a] = (pi[a] << 1) + curplyr->gold);
										centertext(s, b + 18);
										b = 51;
										curplyr++;
									}
									FontSetSys(F_8x10);
									centertext("Black:", 2);
									centertext("Gray:", 40);
									if (*pi < pi[1])
									{
										strcpy(disp, "Gray wins!");
									}
									else if (*pi > pi[1])
									{
										strcpy(disp, "Black wins!");
									}
									else
									{
										strcpy(disp, "The game is drawn.");
									}
									centertext(disp, 78);
									delay(60);
									/*while (kbhit())
									{
										ngetchx();
									}*/
									GKeyFlush();
									etc();
									a = 40;
									break;
								}
							}
							for(k = 0; k < plyr[turn].units; k++)
							{
								unit = plyr[turn].unit + k;
								if (!unit->orders || unit->orders == board)
								{
									rest(unit, info + unit->uindex);
								}
								else
								{
									unit->m2 = 0;
								}
							}
							turn = !turn;
							pre(plyr, tech, info, &map, &sorts, &nums, s);
							/*
							Now this is performed in pre
							if (turnnum == 1)
							{
								gettechnology(plyr, turn, tech, techs, info, infos, &sorts, &keys, sprites, 0);
							}*/
						case 27: case 31: case 32:
							a = getposition(disp, center, plyr, info, &map, (a == 27) - (a == 31));
							break;
						/*case 28:
							a = getunit(&keys, center, plyr, turn, info, infos, tech, techs, sprites, &map, &sorts, &goldoverflow, 0, 0);
							break;*/
						case 29:
							//setdisp(disp, center, plyr, turn, info, &map);
							draw(disp, center, info, &map, plyr);
						case 28:
							a = getunit(center, plyr, info, &nums, tech, disp, &map, &sorts, ((a == 29) << 3));
							break;
						case 33:
							infotbar(curplyr, tech, info, &nums, &sorts, 0, *(sorts.asort), 1);
							a = 27;
							break;
						case 30:
							a = (!plyr->units && !plyr->cities);
							b = (!plyr[1].units && !plyr[1].cities);
							GrayOff();
							/*FontSetSys(F_8x10);
							ClrScr();
							DrawStr(11, 2, "The game is over.", A_NORMAL);*/
							strcpy(s, "The game is over.^");
							if (a)
							{
								if (b)
								{
									/*DrawStr(11, 13, "It has ended in a", A_NORMAL);
									DrawStr(59, 24, "draw.", A_NORMAL);*/
									strcat(s, "It has ended in a^draw.");
								}
								else
								{
									strcat(s, "Gray wins!");
								}
							}
							else
							{
								strcat(s, "Black wins!");
							}
							bigmessage(s);
							delay(40);
							/*while (kbhit())
							{
								ngetchx();
							}*/
							GKeyFlush();
							etc();
							/*a = 40;
							break;*/
						case 40:
							bigmessage("^Civ89");
							setfont6x8();
							centertext("v1.05", 26);
							DrawStrXor(19, 70, "New Game");
							DrawStrXor(86, 70, "Load Game");
							setfont4x6();
							centertext(EDITNOTES, 82);
							centertext(EDITNOTES2, 88);
							#ifdef GAMEPLAYMODIFIED
								centertext("Gameplay modified from original version", 94);
							#endif
							drawrect(18, 68, 68, 78);
							a = 0;
							do
							{
								b = 0;
								k = nograyngetchx();
								if (k == KEY_LEFT)
								{
									b = a;
								}
								else if (k == KEY_RIGHT)
								{
									b = !a;
								}
								if (b)
								{
									a = !a;
									drawrect(18, 68, 68, 78);
									drawrect(85, 68, 141, 78);
								}
							}
							while (k != 13 && k != 264);
							if (k != 264)
							{
								a = 42 - a;
								for(b = 0; b <= 13; disp[b++] = 0);
								disp[6] = disp[8] = 5;
								disp[24] = 0;
							}
							else
							{
								a = 127;
							}
							break;
						case 41:
							nums.game = askwhichdatatoload(0);
							if (nums.game >= 0)
							{
								a = loadgame(plyr, &map, &nums, s);
								if (!a)
								{
									error(4);
									a = 127;
								}
								else if (a == -1)
								{
									a = 40;
								}
								else if (loadscen(nums.scen, &tech, &info, s, &nums, &sorts))
								{
									a = 32;
								}
								else
								{
									error(3);
									a = 127;
								}
							}
							else
							{
								a = 40;
							}
							break;
						case 42:
							setfont4x6();
							ClrScr();
							DrawStrXor1(11, "(set turn limit to 0 to eliminate limit)");
							DrawStrXor1(37, "(a positive handicap makes it easier for black)");
							DrawStrXor1(94, "Use \x15\x16\x17\x18 to move, +- or 0-9 to change");
							setfont6x8();
							DrawStrXor1(2, "Turn Limit:");
							DrawStrXor1(19, "Starting Gold:");
							DrawStrXor1(28, "Handicap:");
							DrawStrXor1(45, "Save File:");
							centertext("Player Names:", 56);
							DrawStrXor1(65, "Black:");
							DrawStrXor1(74, "Gray:");
							centertext("Done", 85);
							for(a = b = c = 0; a <= 7; a++)
							{
								e = b;
								d = c;
								startupxy(&d, &e);
								DrawCharXor(d, e, disp[a] + '0');
								if (c != 3)
								{
									c++;
								}
								else
								{
									b = 1;
									c = 0;
								}
							}
							drawhandicap(disp);
							DrawCharXor(67, 45, disp[12] + '0');
							emptyfull(disp[12], 45);
							DrawStrXor(43, 65, disp + 13);
							DrawStrXor(37, 74, disp + 24);
							a = 6;
							b = e = 0;
							highlight2(b, a);
							do
							{
								c = a;
								d = b;
								k = nograyngetchx();
								e = dirkeys2(k);
								if (e == 2)
								{
									a += 6;
								}
								else if (e == 4)
								{
									a++;
								}
								else if (e == 1)
								{
									b = max(b - 1, 0);
								}
								else if (e)
								{
									if (c <= 1)
									{
										b = min(b + 1, 3);
									}
								}
								else if (a == 4 || a == 5)
								{
									f = 0;
									g = 11 * a - 31;
									j = h = strlen(disp + g);
									e = 9 * a + 29;
									if (k == 257)
									{
										// backspace
										if (h)
										{
											f = disp[g + (j = --h)];
											disp[g + h] = 0;
										}
									}
									else if ((f = keyletter(k)))
									{
										if (isalpha(k))
										{
											f = k;
										}
										if (h < 10)
										{
											sprintf(disp + g + h, "%c", f);
											// The following code does not add the null terminator   disp[g + h] = f;
											if (DrawStrWidth(disp + g, F_4x6) <= 48 + (a << 1))
											{
												j = h + 1;
											}
											else
											{
												disp[g + h] = f = 0;
											}
										}
										else
										{
											f = 0;
										}
									}
									else if (k == 263)
									{
										// Clear
										DrawStrXor(67 - 6 * a, e, disp + g);
										disp[g] = j = 0;
									}
									if (f)
									{
										DrawCharXor(67 + 6 * (h - a), e, f);
									}
									if (j != i)
									{
										cursor(a, i);
										cursor(a, i = j);
									}
								}
								else if (k == '+' || k == '-' || isdigit(k))
								{
									if (a == 2)
									{
										//h = 11;
										if (isdigit(k))
										{
											h = k - '+'; //- '0' + 5;
										}
										else
										{
											h = disp[8];
											if (k == '+')
											{
												h++;
											}
											else
											{
												h--;
											}
										}
										if (h >= 0 && h <= 10)
										{
											drawhandicap(disp);
											disp[8] = h;
											drawhandicap(disp);
										}
									}
									else if (a <= 3 && k - '0' != disp[h = (a << 2) + b])
									{
										f = b;
										g = a;
										startupxy(&f, &g);
										DrawCharXor(f, g, disp[h] + '0');
										if (isdigit(k))
										{
											disp[h] = k - '0';
										}
										else
										{
											if (k == '+')
											{
												disp[h]++;
											}
											else
											{
												disp[h] += 9;
											}
											disp[h] %= 10;
										}
										DrawCharXor(f, g, disp[h] + '0');
										if (a == 3)
										{
											emptyfull(disp[12], 45);
										}
									}
								}
								if (a != c || b != d)
								{
									a %= 7;
									if (b == d)
									{
										b = (c <= 1) * 3;
									}
									highlight2(d, c);
									if (c == 4 || c == 5)
									{
										cursor(c, i);
									}
									highlight2(b, a);
									if (a == 4 || a == 5)
									{
										i = strlen(disp + 11 * a - 31);
										cursor(a, i);
									}
								}
							}
							while ((k != 13 || a != 6) && k != 264);
							if (k == 264)
							{
								a = 40;
								break;
							}
						case 43:
							if ((nums.scen = askwhichdatatoload(1)) == -1)
							{
								a = 42;
								break;
							}
							else
							{
								for(b = 35; b <= 45; disp[b++] = 0);
								if (!loadscen(nums.scen, &tech, &info, s, &nums, &sorts))
								{
									error(3);
									a = 127;
									break;
								}
							}
						case 44:
							ClrScr();
							setfont6x8();
							c = -8;
							b = 1;
							for(a = 0; a < nums.infos && b <= 9; a++)
							{
								if (info[a].tech == 63)
								{
									sprintf(s + 21, "%d %s", disp[34 + b++], info[a].id);
									DrawStrXor1(c += 9, s + 21);
								}
							}
							if ((disp[46] = (b > 1)))
							{
								centertext("Done", 91);
								a = 0;
								highlight(64);
								do
								{
									e = (c = a) + 34;
									if ((d = dirkeys2(k = nograyngetchx())) == 2)
									{
										a += b - 1;
									}
									else if (d == 4)
									{
										a++;
									}
									else if (a && (d || k == '+' || k == '-' || isdigit(k)))
									{
										if (isdigit(k))
										{
											f = k - '0';
										}
										else
										{
											if (d == 3 || k == '+')
											{
												f = disp[e] + 1;
											}
											else
											{
												f = disp[e] + 9;
											}
											f %= 10;
										}
										if ((disp[45] += f - disp[e]) > 25)
										{
											f += 25 - disp[45];
											disp[45] = 25;
										}
										if (f != disp[e])
										{
											DrawCharXor(1, d = 9 * a - 8, disp[e] + '0');
											DrawCharXor(1, d, (disp[e] = f) + '0');
										}
									}
									if ((a %= b) != c)
									{
										highlight(c + 64);
										highlight(a + 64);
									}
								}
								while ((k != 13 || a) && k != 264);
								if (k == 264)
								{
									a = 43;
									break;
								}
							}
						case 45:
							if ((nums.map = askwhichdatatoload(2)) == -1)
							{
								a = 43 + disp[46];
							}
							else if (loadmap(nums.map, &map, plyr))
							{
								turn = nums.goldoverflow = 0;
								for(a = 0; a <= 1; a++)
								{
									curplyr = plyr + a;
									//curplyr->unit = NULL;
									//curplyr->city = NULL;
									//curplyr->researched = NULL;
									//curplyr->active = NULL;
									curplyr->curunit = curplyr->curcity = curplyr->bridge = curplyr->rbonus5 = curplyr->pbonus5 = curplyr->researchp40 = 0;
									curplyr->showhp = 1;
									curplyr->gold = toint(disp + 4);
									curplyr->gpt = 5;
									curplyr->researching = 254;
									strcpy(nums.player[a], disp + a * 11 + 13);
									tryfree(curplyr->active);
									curplyr->active = (unsigned char *)malloc_throw(nums.infos);
									//tryfree(curplyr->unit);
									//curplyr->unit = (struct UNIT *)malloc_throw((curplyr->units = disp[45]) * sizeof(struct UNIT));
									curplyr->units = disp[45];
									unit = curplyr->unit;
									c = 35;
									for(b = 0; b < nums.infos; b++)
									{
										if ((curplyr->active[b] = (info[b].tech == 63)))
										{
											/*DrawChar(0, 50, b + 'a', A_REPLACE);
											ngetchx();*/
											for(d = 1; d <= disp[c]; d++)
											{
												unit->uindex = b;
												unit->hp10 = info[b].d * 30;
												unit->m2 = (info[b].m << 1);
												unit->tindex = 511;
												unit->attacked = unit->orders = unit->percent2 = 0;
												unit->x = curplyr->city->x;
												unit->y = curplyr->city->y;
												unit++;
											}
											c++;
										}
									}
									/*DrawChar(0, 50, disp[45] + 'a', A_REPLACE);
									ngetchx();*/
									redoapos(curplyr, &nums, &sorts);
									tryfree(curplyr->researched);
									curplyr->researched = (unsigned char *)malloc_throw(nums.techs);
									for(b = 0; b < nums.techs; b++)
									{
										curplyr->researched[b] = 0;
									}
								}
								curplyr->handicap = -(plyr->handicap = disp[8] - 5);
								//nums.player[0][0] = nums.player[1][0] = 0;
								//nums.scen = nums.map = nums.game = 0;
								nums.autosave = nums.turnnum = 1;
								nums.game = disp[12];
								nums.maxturn = toint(disp);
								/*for(b = 0; b < techs; b++)
								{
									DrawChar(0, 50, plyr[0].researched[b] + '0', A_REPLACE);
									ngetchx();
								}*/
								// DAD
								//curplyr->city[0].building = 2;
								gettechnology(plyr, tech, info, &nums, &sorts, 0);
								a = 32;
							}
							else
							{
								error(2);
								a = 127;
							}
							break;
						default:
							a = getaction(center[a], plyr, info, &nums, tech, disp, center, &map, &sorts, 0);
					};
				}
				while (a != 127);
			}
			else
			{
				error(6);
			}
			tryfree(tech);
			freeplyr(plyr);
			for(a = 0; a <= 1; a++)
			{
				/* if (nums.player)  This is always true
				{*/
					tryfree(nums.player[a]);
				//}
			}
			tryfree(map.tile);
			tryfree(info);
			tryfree(sprites);
			tryfree(sorts.csort);
			tryfree(sorts.asort);
			//tryfree(sorts.tsort);
			//tryfree(plyr);
			tryfree(nums.player);
		}
		else
		{
			error(5);
		}
	}
	else
	{
		error(5);
	}
	GrayOff();
}
