#include "empire.h"
#include "logic.h"

extern bool ai_unit_animating;
extern int SIDEBAR_WIDTH, BOTTOMBAR_HEIGHT;

bool translucent_units;

int mousetype;
vector<int> movelist;
vector<_unitexplosion> boom;

void gamestate::next_turn()
{
	int iterations = 0;
	
	pturn++;
	while (player[pturn].playing != 1)
	{
		pturn++;
		if (pturn >= 4) pturn = 0;
		
		iterations++;
		if (iterations > 10)
		{
			empdebug("Error: next_turn() is stuck in an infinite loop!  Setting pturn to 0.");
			pturn = 0;
			break;
		}
	}
	
	new_turn = true;
	unitselected = NULL;
	state = NOTHING_HAPPENING;
}

void gamestate::reset()
{
	new_turn = true;
	typingchat = false;
	netclient_multiple_actions = false;
	netclient_response_wait_time = 0;

	if (!loaded_from_file)
		pturn = 0;

	state = NOTHING_HAPPENING;

	attacker = NULL;
	defender = NULL;

	build_menu_factory_tile.x = -1;
	build_menu_factory_tile.y = -1;
	build_menu_factory_type = -1;

	old_mouse_location.x = -1;
	old_mouse_location.y = -1;

	unload_slot = -1;

	windelay = 0;
}



//=============================================================================
int logic()
{
	static bool attempting_reconnect = false;
	static int t, old_logicstate;
	bool show_mouse = false;
	bool draw_sidebar = false;
	bool enable_sidebar = true;
	char c[200];
	DyBytes data;

	if (game->game_just_started)
	{
		game->reset();
		old_logicstate = NOTHING_HAPPENING;
		game->game_just_started = false;
		attempting_reconnect = false;
	}
	if (game->new_turn)
	{  //if it's a new turn
		empdebug("New turn");
		init_logic();
		if (!global.netgame)
		{
			debug_savegame();  //save the game to debug later if the game crashes
		}
		game->new_turn = false;
	}

	for (int i = 0; i < 4; i++)
	{  //check all the units and make sure they're within the map
		if (player[i].playing == 1)
		{
			for (int j = 0; j < 50; j++)
			{
				if (player[i].unit[j].exists == 1)
				{
					if ((player[i].unit[j].tilex < 0) || (player[i].unit[j].tiley < 0) || (player[i].unit[j].tilex > worldmap.get_length()) || (player[i].unit[j].tiley > worldmap.get_height()))
					{  //if not, we've got a problem
						sprintf(c, "HORRIBLE ERROR: Player %d, unit %d (%s) is somehow at location (%d, %d)!", i, j, unitstats[player[i].unit[j].type].name.c_str(), player[i].unit[j].tilex, player[i].unit[j].tiley);
						empdebug(c);
						exit(1);
					}
				}
			}
		}
	}

	if (old_logicstate != game->state)
	{  //if something happened, make sure all building ownership is up to date
		old_logicstate = game->state;
		update_cities();
	}

	if ((game->state == NOTHING_HAPPENING) && (one_team_left()))
	{
		game->state = ONE_TEAM_LEFT;
		game->windelay = 40;
	}

	if ((game->state != IN_OPTIONS_MENU) && (game->state != IN_HELP_SCREEN))
	{
		draw_sidebar = true;
		
		worldmap.draw();

		if (attempting_reconnect)
		{  //if we lost our connection to the host, try to get back together with the other clients
			int status;
			do_sidebar(game->pturn, NULL, true);
			do_particles();

			status = reconnect();
			draw_mouse();
			if (status == 0)
			{  //still trying to reconnect, skip the rest of the logic loop
				return LOGIC;
			}
			else if (status == -1)
			{  //can't continue the game
				attempting_reconnect = false;
				game->game_just_started = true;
				game->new_turn = true;
				return NETWORK;
			}
			else
			{  //reconnected and ready to resume play
				attempting_reconnect = false;
				game->state = NOTHING_HAPPENING;  //when this happens, the state is usually NET_WAITING_FOR_RESPONSE
			}
		}

		if ((game->state == IN_GENERAL_MENU) ||
			(game->state == IN_UNIT_BUILD_MENU) ||
			(game->state == SCROLLING) ||
			(game->state == SAVING_GAME) ||
			(game->state == IN_GENERAL_MENU) ||
			(game->state == IN_BATTLE) ||
			(game->state == ONE_TEAM_LEFT) ||
			(game->state == FADE_EXIT))
		{
			enable_sidebar = false;
		}
		
		if ((global.netgame) && (game->state != BACK_TO_MAIN_MENU) && (game->state != FADE_EXIT))
		{
			logic_netchat();

			if (net.server)
			{
				for (unsigned int i = 0; i < net.engine->getMaxConnections(); i++)
				{
					if (!net.engine->isConnected(i))
					{
						for (int j = 0; j < 3; j++)
						{
							if (net.clientthread[j] == (signed)i)
							{
								empdebug("In logic loop, client");
								empdebug(j);
								empdebug("on thread");
								empdebug(net.clientthread[j]);
								empdebug("is no longer connected.");

								net.clientthread[j] = -1;
								sprintf(c, "(Player %d was disconnected)", j + 2);
								broadcast_chat_message(c);
								logic_netchat(c);

								empdebug("Removing disconnected player's stuff...");
								if (player[j + 1].playing == 1)
								{
									player[j + 1].playing = 0;
									transform_buildings(j + 1, -1);
									
									if (j + 1 == game->pturn)
									{  //if the player was disconnected on his own turn
										game->next_turn();

										data.clear();
										data.appendByte(NET_NEWTURN);
										data.appendByte(game->pturn);
										net.engine->broadcastTCP(data);
									}
									
									game->state = PLAYER_DEFEATED;
									destroy_player_units(j + 1);
								}
								empdebug("Notifying other clients of the disconnect.");

								data.clear();
								data.appendByte(NET_PLAYERGONE);
								data.appendByte(j + 1);
								net.engine->broadcastTCP(data);
							}
						}
					}
				}
			}
			else  //if it's a network client
			{
				if (!net.engine->isConnected(net.thread))
				{
					attempting_reconnect = true;
					return LOGIC;
				}
			}
		}
	}

	if (gameoptions.time != -999)
	{  //if there is a time limit
		if ((game->state != TIME_UP_INDICATOR) && (game->state != AI_TURN) && (game->state != IN_OPTIONS_MENU) && (game->state != IN_HELP_SCREEN))
		{
			int texty = 0;
			
			if (mouse_y < 240)
				texty = 468;

			sprintf(c, "Time Left: %-1ds", game->time / 50);
			buffer_rectfill(318 - text_length(font, c) / 2, texty, text_length(font, c) + 4, 12, DGREY, 0.75);
			buffer_textout_centre_ex(normal, c, 320, texty, WHITE);
			if (game->time < 1)
			{
				game->next_turn();
				game->state = TIME_UP_INDICATOR;
			}
		}
	}
	else
	{
		game->time = 0;  //if it's not being used, just keep it at 0 for simplicity
	}

	if (game->state == NOTHING_HAPPENING)
	{
		if (check_explodes())
			game->state = EXPLODING;
		else if (!player[game->pturn].playing)
		{  //if the explosions mean the end of the current player (eg, surrender to nobody in particular)
			game->next_turn();  //go to the next player
			game->state = EXPLODING;  //make sure nobody can do anything this logic loop
		}
	}

	mousetype = 0;

	switch(game->state)
	{
		case AI_TURN:
			ai();
			if (!ai_unit_animating)
				defeated_player_check();
			show_mouse = true;
			break;
		case NOTHING_HAPPENING:
			game->time--;
			nothing_happening();
			defeated_player_check();
			minimap.check();
			show_mouse = true;
			break;
		case IN_BATTLE:
			battle();
			break;
		case UNIT_SELECTED:
			game->time--;
			unit_selected();
			show_mouse = true;
			break;
		case UNIT_MOVING:
			unit_moving();
			show_mouse = true;
			break;
		case UNIT_MOVING_AND_ATTACKING:
			unit_moving_and_attacking();
			show_mouse = true;
			break;
		case MECHANIC_CAPPING:
			mechanic_capping();
			show_mouse = true;
			break;
		case UNIT_UNLOADING:
			game->time--;
			unit_unloading();
			show_mouse = true;
			break;
		case UNIT_TELEPORTING:
			unit_teleporting();
			show_mouse = true;
			break;
		case TELEPORT_ANIMATION:
			teleport_animation(NULL, -1, -1);
			break;
		case SAVING_GAME:
			if (do_savemenu() == 0)
			{  //if the savemenu has just closed
				game->state = IN_GENERAL_MENU;
			}
			show_mouse = true;
			break;
		case IN_GENERAL_MENU:
			general_menu();
			show_mouse = true;
			break;
		case IN_UNIT_BUILD_MENU:
			game->time--;
			build_menu();
			show_mouse = true;
			break;
		case IN_OPTIONS_MENU:
			if (do_gameoptions(true) == 0)
			{
				//the resolution may have been changed in do_gameoptions(), so reset the sidebar (to reset the button positions)
				do_sidebar(game->pturn, 0, true, true);
				
				//and make sure the map either is centered (if it doesn't take up all the space available) or doesn't leave any gaps
				avoid_map_gaps();
				
				game->state = IN_GENERAL_MENU;
			}
			break;
		case IN_HELP_SCREEN:
			if (help_screen())  //help_screen() returns true when finished
			{
				game->state = IN_GENERAL_MENU;
			}
			break;
		case SCROLLING:
			scroll_to_hq(game->pturn);
			break;
		case ONE_TEAM_LEFT:
			do_particles();
			windelay();
			stop_music();
			break;
		case WINNER:
			do_sidebar(game->pturn, NULL, true);
			return GAMEOVER;
			break;
		case BACK_TO_MAIN_MENU:
			t = 0;
			game->state = FADE_EXIT;
			break;
		case FADE_EXIT:
			if (t < 24)
			{
				buffer_rectfill(0, 0, global.screen_width, global.screen_height, BLACK, float(t * 10) / 255.0);
				t++;
			}
			else
			{
				if (global.netgame)
				{
					if (net.server)
					{
						if (clientconnected(0)) net.engine->disconnectHost(net.clientthread[0]);
						if (clientconnected(1)) net.engine->disconnectHost(net.clientthread[1]);
						if (clientconnected(2)) net.engine->disconnectHost(net.clientthread[2]);
					}
					else
					{
						net.engine->disconnectHost(net.thread);
					}
				}
				
				buffer_clear_to_color(BLACK);
				player[0].reset();
				player[1].reset();
				player[2].reset();
				player[3].reset();
				game->game_just_started = true;
				play_song(TITLE_THEME);
				return MENU;
			}
			break;
		case TIME_UP_INDICATOR:
			time_up();
			break;
		case EXPLODING:
			if (do_explodes() == 1)
			{  //if the explosion has finished
				game->state = NOTHING_HAPPENING;
			}
			break;
		case PLAYER_DEFEATED:  //this state is no longer needed
			game->state = NOTHING_HAPPENING;
			break;
			/*case NET_BATTLE:
				client_netbattle();
				break;*/
		case START_TURN_SYNC:
			if (sync_burning())
			{
				game->state = NOTHING_HAPPENING;
			}
			break;
		case NET_WAITING_FOR_RESPONSE:
			game->netclient_response_wait_time++;
			
			draw_net_icon(XTILES * MAP_TILE_SIZE - 80, 0);
			netplayer();
			show_mouse = true;
			
			if (game->netclient_response_wait_time > 500)
			{  //if we've been waiting for a response for >10 seconds, assume something went wrong
				const string errstr = "Error: We've been waiting for a response from the host for 10 seconds now.  Assuming something went wrong.";
				
				empdebug(errstr);
				logic_netchat(errstr);
				
				game->netclient_response_wait_time = 0;
				game->state = NOTHING_HAPPENING;
			}
			else if (game->state != NET_WAITING_FOR_RESPONSE)
			{  //we got a response
				game->netclient_response_wait_time = 0;
			}
			break;
		default:
			empdebug("ERROR: Hit unhandled game state in logic loop");
			empdebug(int(game->state));
			break;
	}
	
	if (draw_sidebar)
	{
		if (player[game->pturn].controller == HUMAN)
			do_sidebar(game->pturn, game->unitselected, !enable_sidebar);
		else
			do_sidebar(game->pturn, NULL, !enable_sidebar);
	}

	if ((game->state != IN_UNIT_BUILD_MENU) && (game->state != IN_OPTIONS_MENU) && (game->state != IN_HELP_SCREEN))
	{
		do_particles();
	}

	if (show_mouse)
	{
		draw_mouse(mousetype);
	}

	return LOGIC;
}


void defeated_player_check()
{
	for (int i = 0; i < 4; i++)
	{  //check each player to see if they are not beaten
		if (player[i].playing == 1)
		{
			if (player[i].defeated(i))
			{  //if they are defeated, but still on the map
				player[i].playing = 0;
				transform_buildings(i, -1);
				if (i == game->pturn)
				{  //if the player was defeated on his own turn
					game->next_turn();
				}
				game->state = PLAYER_DEFEATED;
				
				destroy_player_units(i);
			}
		}
	}
}


bool one_team_left()
{
	int aliveteam = -1;
	
	for (int i = 0; i < 4; i++)
	{  //this loop sees if there's more than one team left in the game
		if (player[i].playing == 1)
		{
			if (aliveteam != -1)
			{
				if (player[i].team != aliveteam)
				{
					return false;  //found a different team, so more than one team left
				}
			}
			else
			{  //the team of the first player in the game
				if (aliveteam == -1)
				{
					aliveteam = player[i].team;
				}
			}
		}
	}

	return true;
}


void time_up()
{
	static bool first = true;
	static int t;
	
	if (first)
	{
		t = 50;
		first = false;
		play_sound(S_TANKFIRE);
	}
	
	buffer_textout_centre_ex(big, "TIME UP", 320, 200, WHITE);
	
	t--;
	if (t < 0)
	{
		game->new_turn = true;
		game->state = NOTHING_HAPPENING;
		first = true;
	}
}

void init_logic()
{
	int z, i, j;
	char str[15];

	game->unload_slot = 0;
	game->unitselected = NULL;

	if (!((player[game->pturn].controller == AI) && (player[game->pturn].units_in_play() > 0)))
	{  //if the player is human or an AI with no units, scroll to the HQ
		game->state = SCROLLING;  //sets it to scroll to the current player's HQ
		worldmap.draw();
	}

	check_scrolling(worldmap.camera.get_scroll_x(), worldmap.camera.get_scroll_y());  //if I don't do this, the static floats in check_scrolling aren't correct and bad things happen

	game->new_turn = false;

	if (!game->loaded_from_file)  //if the game was saved and loaded, don't refresh units
	{  //otherwise, it's the start of a new turn, and units need refreshing
		for (int i = 0; i < 4; i++)
		{
			if (player[i].playing)
				player[i].ready_units();
		}
	}

	if (gameoptions.time != -999)
		game->time = gameoptions.time;

	boom.resize(0);  //remove all explosions from existence
	clear_nonunit_particles();  //same for particles, except for unit-animation-related particles
	update_cities();

	stop_music();
	switch(player[game->pturn].number)
	{
		case 0:
			play_song(CRIMSON_THEME);
			break;
		case 1:
			play_song(COBALT_THEME);
			break;
		case 2:
			play_song(PINE_THEME);
			break;
		default:
			{
				stringstream errstr;
				errstr << "Error: Bad song index (" << player[game->pturn].number << ")";
				empdebug(errstr.str());
			}
			break;
	}
	
	sprintf(str, "Player %d", game->pturn + 1);
	create_particle((global.screen_width - SIDEBAR_WIDTH) / 2 - normal.Width(str) / 2, global.screen_height / 2, P_FADETEXT, player[game->pturn].tintcolor, str);

	if (!game->loaded_from_file)  //likewise, the player does not receive more cash
	{                             //and units are not repaired when the game is loaded
		int morecash = money_producing_buildings_owned(game->pturn) * gameoptions.funding;
		player[game->pturn].cash += morecash;
		player[game->pturn].stats.raised += morecash;

		for (int y = 0; y < worldmap.get_height(); y++)
		{
			for (int x = 0; x < worldmap.get_length(); x++)
			{
				_tile *t = worldmap.get_tile(x, y);
				if (t->owned_by(game->pturn))
				{
					if ((t->is_building(CITY)) || (t->is_building(HQ)))
					{
						create_particle(x * MAP_TILE_SIZE + 10,	y * MAP_TILE_SIZE, P_CASH);

						if (t->is_building(CITY))
						{
							t->turnsused++;
							if ((game->cashturns != 0) && (t->turnsused > game->cashturns))
							{  //oil derrick all used up
								init_deadoilderrick(x, y);
								z = t->unit_here();
								t->reset();
								t->x = x;
								t->y = y;
								if (z != -1)
								{
									t->set_unit(z / 100, z % 100);
								}
							}
						}
					}
				}
			}
		}
		z = 0;
		while (z < 50)
		{
			if (player[game->pturn].unit[z].exists == 1)
			{
				player[game->pturn].unit[z].attackdelay--;
				if (player[game->pturn].unit[z].attackdelay < 0) player[game->pturn].unit[z].attackdelay = 0;

				if (unitstats[player[game->pturn].unit[z].type].name == "Mechanic")
				{
					i = 0;
					while (i < 4)
					{
						int x = player[game->pturn].unit[z].tilex;
						int y = player[game->pturn].unit[z].tiley;
						if (i == 0) x++;
						if (i == 1) x--;
						if (i == 2) y++;
						if (i == 3) y--;

						if ((x > 0) && (x < worldmap.get_length()) && (y > 0) && (y < worldmap.get_height()))
						{
							j = any_unit_here(x, y);
							if ((j != -1) && (player[j / 100].team == player[game->pturn].team) && (player[j / 100].unit[j % 100].health < 10))
							{
								player[j / 100].unit[j % 100].health += 2;
								if (player[j / 100].unit[j % 100].health > 10)
								{
									player[j / 100].unit[j % 100].health = 10;
								}
								create_particle(x * MAP_TILE_SIZE, y * MAP_TILE_SIZE, P_REPAIR);
							}
						}
						i++;
					}
				}
			}
			z++;
		}

		if (player[game->pturn].cash > 1000000) player[game->pturn].cash = 1000000;
		repair_units(game->pturn);

		if (!global.netgame)
		{  //burning has randomness in it, so the host needs to be the one to decide
			update_burning();
		}
		else
		{  //plus, we might as well make sure everybody sees the same thing at the start of each turn
			game->state = START_TURN_SYNC;
		}
	}
	
	if (game->loaded_from_file)  //next turn, it won't matter whether the game
	{                            //was originally loaded from a file or not
		game->time = game->loaded_time;
		game->loaded_from_file = false;
	}
	
	if (player[game->pturn].controller != HUMAN) position_mouse(global.screen_width / 2, global.screen_height / 2);
}

void battle()
{
	if (do_battle(game->attacker, game->defender) == 0)  //if the battle just ended
	{
		game->state = NOTHING_HAPPENING;
	}
}

void windelay()
{
	if (game->windelay <= 0)
	{
		game->state = WINNER;
	}
	else
	{
		do_explodes();
		game->windelay--;
	}
}

void ai()
{
	DyBytes data;
	//bool can_rightclick = true;
	int temp1 = do_ai(game->pturn);
	int indicator = temp1 / 10;  //if the AI uses its power, it indicates that in temp1's tens column
	
	/*if (indicator == 2)  //signals that the AI is in a battle or scroll animation
	{
		can_rightclick = false;  //no bringing up the menu in a battle animation!
	}
	else*/ if (indicator == 3)  //signals that there's an explosion about to happen
	{
		game->state = EXPLODING;
		//can_rightclick = false;
	}

	/*if ((can_rightclick) && (mouse_rclicked()))
  	{
		game->state = AI_MENU;
  	}*/

	temp1 %= 10;  //ignore the indicator information and just get a 1 or 0
	if (temp1 == 1)  //if do_ai() has indicated that it is finished
	{
		if (global.netgame && !net.server)
		{
			data.clear();
			data.appendByte(NET_CLIENT_ENDTURN);
			net.engine->sendTCP(net.thread, data);
			game->state = NET_WAITING_FOR_RESPONSE;
		}
		else
		{
			game->state = NOTHING_HAPPENING;

			game->next_turn();

			if (global.netgame)
			{
				data.clear();
				data.appendByte(NET_NEWTURN);
				data.appendByte(game->pturn);
				net.engine->broadcastTCP(data);
			}
		}
	}
}

void nothing_happening()
{
	static _unit *mechanicmoves = 0;
	_unit *u = 0;
	_tile *tile = 0;
	
	int mx = mouse_x;
	int my = mouse_y;
	check_scrolling();
	
	if (player[game->pturn].controller == AI)
	{  //if the AI turn just started, set the logicstate accordingly
		game->state = AI_TURN;
		return;
	}
	else if (global.netgame)
	{  //if it's a netplayer turn, check the network
		netplayer();
		if ((player[game->pturn].controller == NET) || (player[game->pturn].controller == NETAI))
		{
			draw_wait_icon(XTILES * MAP_TILE_SIZE - 80, 0);
		}
	}
	
	if ((mx < SCREEN_W - SIDEBAR_WIDTH) && (my < SCREEN_H - BOTTOMBAR_HEIGHT))
	{
		tile = worldmap.get_mouseover_tile(mx, my);
		
		if (tile != 0)
			u = tile->unit_here_ptr();
	}
	
	if (tile == 0)
		return;
	
	if (u != 0)
	{
		draw_unitstats(u, mx, my);
	}
	else if (player[game->pturn].controller == HUMAN)
	{
		if ((tile->is_unit_producing()) && (tile->owned_by(game->pturn)))
			mousetype = 1;
	}
	
	if (translucent_units && !(mouse_b & 2)) translucent_units = false;
	if ((mechanicmoves != NULL) && !(mouse_b & 2)) mechanicmoves = NULL;
	
	if (mouse_b & 2)
	{  //draws a unit's attack range
		if (u != 0)
		{  //if the mouse is over a unit, show its range
			if (mechanicmoves != u)
			{  // "mechanicmoves": this originally only showed the movement range of Mechanics
				mechanicmoves = u;
				worldmap.create_limited_pathmap(u->owner, unitstats[u->type].movetype, u->tilex, u->tiley, u->move);
			}

			show_movable_tiles(u, 0.4);
			if ((unitstats[u->type].attacktype != -1) || (unitstats[u->type].name == "Mechanic"))
			{
				draw_attackrange(u);
			}

			if (unitstats[u->type].name == "Shield Generator")
			{
				draw_shieldrange(u);
			}
			translucent_units = false;
		}
		else
		{  //otherwise, make it easier to see the terrain
			translucent_units = true;
		}
	}
	else if (mouse_clicked() && (player[game->pturn].controller == HUMAN))
	{
		if ((u != 0) && (u->owner == game->pturn))
		{  //if one of the current player's units is here
			if (u->ready == 1)
			{  //if the here is ready, select it
				game->unitselected = u;
				worldmap.create_limited_pathmap(game->pturn, unitstats[u->type].movetype, u->tilex, u->tiley, u->move + 1);
				game->state = UNIT_SELECTED;
			}
		}
		else if (u == 0)
		{  //if there is no unit here, check for a building that can make units
			if ((tile->is_unit_producing()) && (tile->owned_by(game->pturn)))
			{
				game->build_menu_factory_type = tile->building_type();
				game->state = IN_UNIT_BUILD_MENU;
				
				game->build_menu_factory_tile.x = tile->x;
				game->build_menu_factory_tile.y = tile->y;
				game->old_mouse_location.x = mx;
				game->old_mouse_location.y = my;
			}
		}
	}
}

void build_menu()
{
	int t = do_unitmenu(game->build_menu_factory_type, game->build_menu_factory_tile.x, game->build_menu_factory_tile.y, game->pturn);
	
	if (t != 0)  //do_unitmenu should return 1 when a unit has been
	{            //created or -1 if the player cancels the unitmenu
		game->state = NOTHING_HAPPENING;

		if ((t == 1) && (global.netgame) && (!net.server))
		{  //if it wants to create a unit, but is a client in a netgame, wait for a server response
			game->state = NET_WAITING_FOR_RESPONSE;
		}
	}
}

void general_menu()
{
	string s = do_generalmenu();
	DyBytes data;

	if (s != "-")
	{
		if (s == "Quit")
		{
			if (global.netgame)
			{
				if (net.server)
				{
					if (clientconnected(0)) net.engine->disconnectHost(net.clientthread[0]);
					if (clientconnected(1)) net.engine->disconnectHost(net.clientthread[1]);
					if (clientconnected(2)) net.engine->disconnectHost(net.clientthread[2]);
				}
				else
				{
					net.engine->disconnectHost(net.thread);
				}
			}
			game->state = BACK_TO_MAIN_MENU;
		}
		else if (s == "Options")
		{
			game->state = IN_OPTIONS_MENU;
		}
		else if (s == "Help")
		{
			game->state = IN_HELP_SCREEN;
		}
		else if (s == "Save")
		{
			game->state = SAVING_GAME;
		}
		else if (s == "Surrender")
		{
			if (!global.netgame || (global.netgame && net.server))
			{
				if (global.netgame)
				{
					data.clear();
					data.appendByte(NET_PLAYER_SURRENDER);
					data.appendByte(game->pturn);
					data.appendByte(global.surrenderval);
					net.engine->broadcastTCP(data);
				}

				do_surrender(game->pturn, global.surrenderval);
				game->state = NOTHING_HAPPENING;
			}
			else
			{
				data.clear();
				data.appendByte(NET_CLIENT_SURRENDER);
				data.appendByte(global.surrenderval);
				net.engine->sendTCP(net.thread, data);
				game->state = NET_WAITING_FOR_RESPONSE;
			}
		}
		else if (s == "Cancel")
		{
			game->state = NOTHING_HAPPENING;
		}
	}
}

void unit_moving_and_attacking()
{
	unit_moving();
		
	if (game->state != UNIT_MOVING_AND_ATTACKING)
	{  //unit_moving resets the game state when it's finished, so movement phase is over
		game->unitselected = NULL;
		game->state = IN_BATTLE;
		//game->attacker and game->defender have already been set
		//sending battle data is taken care of in the battle function itself
	}
}

void unit_moving()
{
	bool deselect = false;
	int z;
	_tile *tile;
	_unit *u = game->unitselected;

	u->do_movement();
	u->draw();
	if (!u->is_moving())  //if the unit has just stopped moving
	{
		tile = worldmap.get_tile(u->tilex, u->tiley);
		
		game->old_mouse_location.x = mouse_x;
		game->old_mouse_location.y = mouse_y;
		z = unit_here_thorough(game->pturn, u->tilex, u->tiley, u->number);

		if (z != -1)  //another unit is on this tile
		{
			game->unitselected = NULL;
			game->state = NOTHING_HAPPENING;

			if (player[game->pturn].unit[z].can_load_unit(u) == 1)
			{  //if it can be loaded, load
				player[game->pturn].unit[z].load_unit(u);
				tile->set_unit(game->pturn, z);  //makes sure unitmap is
				u->exists = 0;                   //correct for this spot
				u->health = 0;
			}
			else
			{  //otherwise, this must be a merge
				merge_units(u, u->tilex, u->tiley);
				if (u->ready == 1)
				{
					if (player[u->owner].controller == HUMAN)
					{
						game->unitselected = u;
						game->state = UNIT_SELECTED;
					}
				}
			}
			return;
		}

		if (unitstats[u->type].attacktype == RANGED)
		{  //if it's a ranged attacker, it can't do anything after moving
			u->ready = 0;
			game->unitselected = NULL;
			game->state = NOTHING_HAPPENING;
			return;
		}

		if (unitstats[u->type].cancapture)
		{
			if ((tile->is_building()) && ((tile->is_unit_producing()) || (tile->is_HQ())))
			{  //if it's a building that gets permanently captured
				if (!tile->owned_by(u->owner))
				{  //if it's not owned by the player
					if (tile->is_HQ())
					{  //if we don't do this, winning the game by HQ capture doesn't happen until you deselect the unit
						deselect = true;
					}
					capture_building(u);
				}  // (this means you can capture teammates' buildings)
			}
		}
		
		if (game->netclient_multiple_actions)
		{
			deselect = true;
			game->netclient_multiple_actions = false;
		}

		if ((player[u->owner].controller != HUMAN) || deselect)
		{  //if it was a network player's unit or an AI, or the unit should be deselected for another reason
			game->unitselected = NULL;
			game->state = NOTHING_HAPPENING;
			return;
		}
		game->state = UNIT_SELECTED;
	}
}

void unit_selected()
{
	static bool first = true;
	static float sel, seldir;
	static vector<_loc> attackables;

	int mx = mouse_x;
	int my = mouse_y;
	int x, y, z, dx, dy, damage;
	char damagepercent[10];
	bool ok;
	_unit *u = game->unitselected;
	_tile *tile = 0;
	DyBytes data;

	if (first)
	{
		attackables = find_attackables(u);
		do_moveselect(-1, -1, -1, -1);  //this resets do_moveselect's static ints
		sel = 0.5;
		seldir = 0.02;
		first = false;
	}

	x = (u->tilex - worldmap.camera.get_scroll_x()) * MAP_TILE_SIZE + worldmap.camera.get_offset_x();
	y = (u->tiley - worldmap.camera.get_scroll_y()) * MAP_TILE_SIZE + worldmap.camera.get_offset_y();
	buffer_rect(x, y, x + MAP_TILE_SIZE, y + MAP_TILE_SIZE, WHITE, sel);
	sel += seldir;
	if ((sel >= 1.0) || (sel <= 0.5))
	{
		seldir *= -1;
	}

	draw_attackables(attackables);
	
	if ((mx < SCREEN_W - SIDEBAR_WIDTH) && (my < SCREEN_H - BOTTOMBAR_HEIGHT))
	{
		tile = worldmap.get_mouseover_tile(mx, my);
	}
	
	if (tile != 0)
	{
		if (u->canmove)
		{
			do_moveselect(game->pturn, u->number, tile->x, tile->y);
			
			_unit *tileunit = tile->unit_here_ptr();
			if ((tileunit != 0) && (tile->get_step() <= u->move) && (can_merge(u, tileunit)))
			{
				if (my < global.screen_height - 20)
				{
					buffer_textout_ex(med, "MERGE", mx + 14, my + 14, BLACK);
					buffer_textout_ex(med, "MERGE", mx + 15, my + 15, WHITE);
				}
				else
				{
					buffer_textout_ex(med, "MERGE", mx + 14, my + 14, BLACK);
					buffer_textout_ex(med, "MERGE", mx + 15, my - 15, WHITE);
				}
			}
			else if ((tileunit != 0) && (tileunit->owner != u->owner) && (tile_distance(u->tilex, u->tiley, tile->x, tile->y) > 1) &&
					(u->attackdelay == 0) && (unitstats[u->type].attacktype == DIRECT) && (type_can_attack(u, tileunit)))
			{  //if there is a unit here that the selected unit (a direct attacker) could move to and attack
				bool valid_move_to_target = false;
				_loc movement_end_tile = movelist_result(u);
				
				if ((tile_distance(movement_end_tile.x, movement_end_tile.y, tile->x, tile->y) != 1) ||
				    (worldmap.get_tile(movement_end_tile.x, movement_end_tile.y)->unit_here() != -1))
				{  //if the current movelist doesn't put us next to the target or on an empty tile, create a path that will
					_loc nearesttile = nearest_tile_to(_loc(tile->x, tile->y));
					if (nearesttile.valid())
					{
						vector<_aipath> t = best_aipath(game->pturn, unitstats[u->type].movetype, u->tilex, u->tiley, nearesttile.x, nearesttile.y);
						
						movelist.resize(0);
						for (unsigned int i = 0; i < t.size(); i++)
							movelist.push_back(t[i].dir);
						
						valid_move_to_target = true;
					}
				}
				else
				{
					valid_move_to_target = true;
				}
				
				if (valid_move_to_target)
				{
					glb.draw(58, (tile->x - worldmap.camera.get_scroll_x()) * MAP_TILE_SIZE + worldmap.camera.get_offset_x(),
							     (tile->y - worldmap.camera.get_scroll_y()) * MAP_TILE_SIZE + worldmap.camera.get_offset_y());
					
					if (gameoptions.show_damage)
					{
						damage = int(total_damage(u, tileunit));
						sprintf(damagepercent, "%#.1f%%", float(damage));
						dx = mx + 14 - worldmap.camera.get_offset_x();
						dy = my + 14 - worldmap.camera.get_offset_y();
						buffer_rectfill(dx - 1, dy, dx + normal.Width(damagepercent) + 1, dy + normal.Height(damagepercent) + 1, BLACK, 0.5);
						buffer_textout_ex(normal, damagepercent, dx, dy, WHITE);
						buffer_rect(dx - 1, dy, dx + normal.Width(damagepercent) + 1, dy + normal.Height(damagepercent) + 1, DGREY);
					}
				}
			}
		}
		if (gameoptions.show_damage)
		{
			z = attackable_unit_here(u, tile->x, tile->y, true);
			if (z != -1)
			{
				if (unitstats[u->type].attacktype == RANGED)
				{  //if the unit is a ranged attacker and a target is here
					do_moveselect(-1, -1, -1, -1);  //reset moveselect and get rid of any path
				}
				
				glb.draw(58, (tile->x - worldmap.camera.get_scroll_x()) * MAP_TILE_SIZE + worldmap.camera.get_offset_x(),
						     (tile->y - worldmap.camera.get_scroll_y()) * MAP_TILE_SIZE + worldmap.camera.get_offset_y());
				
				damage = int(total_damage(u, &player[z / 100].unit[z % 100]));
				sprintf(damagepercent, "%#.1f%%", float(damage));
				dx = mx + 14 - worldmap.camera.get_offset_x();
				dy = my + 14 - worldmap.camera.get_offset_y();
				buffer_rectfill(dx - 1, dy, dx + normal.Width(damagepercent) + 1, dy + normal.Height(damagepercent) + 1, BLACK, 0.5);
				buffer_textout_ex(normal, damagepercent, dx, dy, WHITE);
				buffer_rect(dx - 1, dy, dx + normal.Width(damagepercent) + 1, dy + normal.Height(damagepercent) + 1, DGREY);
			}
		}
	}
	else
	{  //if the unit is selected, but the mouse is on the sidebar or bottom bar
		if (u->canmove)
		{
			show_movable_tiles(u);
			draw_path(u);
			u->draw(false);
		}
	}
	check_scrolling();

	if (mouse_clicked() && (tile != 0))
	{
		int tileunit = tile->unit_here();
		_unit *tileunit_ptr = tile->unit_here_ptr();
		
		if ((tileunit != -1) && (u->owner != tileunit_ptr->owner))
		{  //if there's an enemy (or annoying teammate) on the clicked tile
			if ((unitstats[u->type].attacktype == DIRECT) && ((tile_distance(u->tilex, u->tiley, tile->x, tile->y) > 1) || (movelist.size() > 0)))
			{  //if the player wants wants to attack that unit with a direct attacker, and the target is beyond one tile or has a movement path (move, then attack, with one click)
				if (u->canmove && (u->attackdelay == 0) && (type_can_attack(u, tileunit_ptr)))
				{  //if the unit is capable of moving and attacking, and could theoretically hit the enemy
					_loc movement_end_tile = movelist_result(u);
					
					if (tile_distance(movement_end_tile.x, movement_end_tile.y, tile->x, tile->y) != 1)
					{  //if the current movelist won't put this unit right next to its target, create a path that will
						_loc nearesttile = nearest_tile_to(_loc(tile->x, tile->y));
						if (nearesttile.valid())
						{
							vector<_aipath> t = best_aipath(game->pturn, unitstats[u->type].movetype, u->tilex, u->tiley, nearesttile.x, nearesttile.y);
							
							movelist.resize(0);
							for (unsigned int i = 0; i < t.size(); i++)
								movelist.push_back(t[i].dir);
						}
					}
					
					move_unit(u);
					
					if ((!global.netgame) || (global.netgame && net.server))
					{
						game->state = UNIT_MOVING_AND_ATTACKING;
						game->attacker = u;
						game->defender = tileunit_ptr;
					}
					else
					{  //as a network client, move_unit already sends the move reques tto the server, now send the attack request
						data.clear();
						data.appendByte(NET_CLIENT_ATTACK);
						data.appendByte(movement_end_tile.x);
						data.appendByte(movement_end_tile.y);
						data.appendByte(tile->x);
						data.appendByte(tile->y);
						net.engine->sendTCP(net.thread, data);
						game->netclient_multiple_actions = true;  //so that the unit is deselected after the movement is complete (so it can go into battle)
						game->state = NET_WAITING_FOR_RESPONSE;
					}
					first = true;
				}
			}
			else
			{  //standard attack
				z = attackable_unit_here(u, tile->x, tile->y, true);
				if (z != -1)
				{  //this check is needed: just because it's an enemy doesn't mean it's attackable
					if ((!global.netgame) || (global.netgame && net.server))
					{
						game->state = IN_BATTLE;
						game->attacker = u;
						game->defender = &player[z / 100].unit[z % 100];
	
						//sending battle data is taken care of in the battle function itself
					}
					else
					{
						data.clear();
						data.appendByte(NET_CLIENT_ATTACK);
						data.appendByte(u->tilex);
						data.appendByte(u->tiley);
						data.appendByte(player[z / 100].unit[z % 100].tilex);
						data.appendByte(player[z / 100].unit[z % 100].tiley);
						net.engine->sendTCP(net.thread, data);
						game->state = NET_WAITING_FOR_RESPONSE;
					}
					game->unitselected = NULL;
					first = true;
				}
			}
		}
		else if (u->canmove)
		{
			ok = false;
			
			if ((tile->x == u->tilex) && (tile->y == u->tiley))
			{  //make sure the unit can't move to the tile it's already on
				ok = false;
			}
			else if ((tileunit != -1) && (tileunit_ptr->owner == game->pturn))
			{  //if another of the player's units is here
				if (movelist.size() == 0)  //the unit hasn't moved at all, so the unit
				{                          //detected on the tile is just the selected unit
					ok = true;
				}
				else if (tileunit_ptr->can_load_unit(u))
				{  //if a unit is here but can load the selected unit, everything's OK
					ok = true;
				}
				else if (tileunit_ptr->type == u->type)
				{  //or if it is the same unit type and is damaged, they might be able tp merge
					ok = can_merge(u, tileunit_ptr);
				}
			}
			else
			{  //there is no unit or an allied unit here
				if (tile->get_step() <= u->move)
				{  //only have movement if the mouse is on a movable tile
					ok = true;
				}
				if (tileunit != -1)
				{  //if there is an allied unit here
					ok = false;
				}
			}
			if (ok && (movelist.size() > 0))  //so we can tell if a unit has been ordered to
			{                                 //move, which is important for ranged-attack units
				move_unit(u);
				first = true;
			}
			else  //clicked on a tile that can't be moved to and with no enemy: deselect current unit
			{
				game->unitselected = NULL;
				game->state = NOTHING_HAPPENING;
				first = true;
			}
		}
		else  //clicked on a tile that can't be moved to and with no enemy: deselect current unit
		{
			game->unitselected = NULL;
			game->state = NOTHING_HAPPENING;
			first = true;
		}
	}

	if (mouse_rclicked())  //right-clicked the mouse: deselect current unit
	{
		first = true;
		game->unitselected = NULL;
		game->state = NOTHING_HAPPENING;
	}
}

_loc movelist_result(_unit *u)
{
	_loc final(u->tilex, u->tiley);
	
	for (unsigned int i = 0; i < movelist.size(); i++)
	{
		switch(movelist[i])
		{
			case UP:
				final.y--;
				break;
			case DOWN:
				final.y++;
				break;
			case LEFT:
				final.x--;
				break;
			case RIGHT:
				final.x++;
				break;
			default:
				{
					stringstream s;
					s << "Error in movelist_result.  Movelist entry is " << movelist[i];
					empdebug(s.str());
				}
				break;
		}
	}
	
	return final;
}

void move_unit(_unit *u)
{  //the movement commands are in the 'movelist' global
	if (!global.netgame)
	{
		u->canmove = false;
		play_moving_sound(u->type);
		u->set_moves(movelist);
		game->state = UNIT_MOVING;
	}
	else
	{  //a netgame
		DyBytes data;
		
		if (net.server)
		{
			u->canmove = false;
			play_moving_sound(u->type);
			u->set_moves(movelist);
			game->state = UNIT_MOVING;

			data.clear();
			data.appendByte(NET_MOVEUNIT);
			data.appendByte(u->tilex);
			data.appendByte(u->tiley);
			data.appendByte(movelist.size());
			
			for (unsigned int z = 0; z < movelist.size(); z++)
				data.appendByte(movelist[z]);
			
			net.engine->broadcastTCP(data);
		}
		else
		{  //client just requests that the unit be moved
			data.clear();
			data.appendByte(NET_CLIENT_MOVEUNIT);
			data.appendByte(u->tilex);
			data.appendByte(u->tiley);
			data.appendByte(movelist.size());
			
			for (unsigned int z = 0; z < movelist.size(); z++)
				data.appendByte(movelist[z]);
			
			net.engine->sendTCP(net.thread, data);

			game->unitselected = NULL;
			game->state = NET_WAITING_FOR_RESPONSE;  //the client must wait for the server to approve the movement
		}
	}
}

_loc nearest_tile_to(_loc target)
{  //pick out the best empty tile adjacent to the target tile (assumes pathmap is filled in)
	int best = UNREACHABLE;
	_loc bestloc;
	
	_loc a(target.x, target.y - 1);
	_loc b(target.x, target.y + 1);
	_loc c(target.x - 1, target.y);
	_loc d(target.x + 1, target.y);
	
	if (a.valid() && (worldmap.get_tile(a.x, a.y)->unit_here() == -1) && (worldmap.get_tile(a.x, a.y)->get_step() < best))
	{
		bestloc = a;
		best = worldmap.get_tile(a.x, a.y)->get_step();
	}
	if (b.valid() && (worldmap.get_tile(b.x, b.y)->unit_here() == -1) && (worldmap.get_tile(b.x, b.y)->get_step() < best))
	{
		bestloc = b;
		best = worldmap.get_tile(b.x, b.y)->get_step();
	}
	if (c.valid() && (worldmap.get_tile(c.x, c.y)->unit_here() == -1) && (worldmap.get_tile(c.x, c.y)->get_step() < best))
	{
		bestloc = c;
		best = worldmap.get_tile(c.x, c.y)->get_step();
	}
	if (d.valid() && (worldmap.get_tile(d.x, d.y)->unit_here() == -1) && (worldmap.get_tile(d.x, d.y)->get_step() < best))
	{
		bestloc = d;
		best = worldmap.get_tile(d.x, d.y)->get_step();
	}
	
	return bestloc;
}

void unit_unloading()
{
	static bool first = true;
	static vector<_loc> locations;
	int mx = mouse_x;
	int my = mouse_y;
	DyBytes data;
	_unit *u = game->unitselected;

	if (first)
	{
		first = false;
		locations.clear();
		locations.reserve(2);
		locations = find_unload_locations(u, u->load[game->unload_slot].type);
	}
	show_unload_locations(locations);
	
	if (mouse_clicked() && (mx < SCREEN_W - SIDEBAR_WIDTH) && (my < SCREEN_H - BOTTOMBAR_HEIGHT))
	{
		_tile *tile = worldmap.get_mouseover_tile(mx, my);
		
		if (tile == 0)
			return;
		
		if (unit_unloadable_here(u->tilex, u->tiley, tile->x, tile->y, u->load[game->unload_slot].type))
		{
			if ((!global.netgame) || (global.netgame && net.server))
			{
				u->unload_unit(game->unload_slot, tile->x, tile->y);

				if (global.netgame)
				{  //server in a netgame
					data.clear();
					data.appendByte(NET_UNLOAD);
					data.appendByte(u->tilex);
					data.appendByte(u->tiley);
					data.appendByte(game->unload_slot);
					data.appendByte(tile->x);
					data.appendByte(tile->y);
					net.engine->broadcastTCP(data);
				}

				// If something is exploding, that must mean the unloaded unit can capture, and was unloaded on an HQ
				// In which case, the APC needs to be deselected so the explosion animations can play
				if (!check_explodes()) game->state = UNIT_SELECTED;
				else
				{
					game->unitselected = NULL;
					game->state = NOTHING_HAPPENING;
				}
			}
			else
			{  //client in a netgame
				data.clear();
				data.appendByte(NET_CLIENT_UNLOAD);
				data.appendByte(u->tilex);
				data.appendByte(u->tiley);
				data.appendByte(game->unload_slot);
				data.appendByte(tile->x);
				data.appendByte(tile->y);
				net.engine->sendTCP(net.thread, data);
				game->state = NET_WAITING_FOR_RESPONSE;
			}

			first = true;
		}
	}

	if (mouse_rclicked())
	{
		first = true;
		game->unload_slot = -1;
		game->state = NOTHING_HAPPENING;
	}
	check_scrolling();
}

void mechanic_capping()
{
	_unit *u = game->unitselected;
	static bool first = true, good[4];
	int x, y;
	int mx = mouse_x;
	int my = mouse_y;
	int i, j, z;
	DyBytes data;

	if (first)
	{
		i = 0;
		while (i < 4)
		{
			good[i] = false;
			x = u->tilex;
			y = u->tiley;

			if (i == 0) x++;
			if (i == 1) x--;
			if (i == 2) y++;
			if (i == 3) y--;

			if ((x > 0) && (x < worldmap.get_length()) && (y > 0) && (y < worldmap.get_height()))
			{
				j = any_unit_here(x, y);
				if ((j != -1) && (player[j / 100].team != player[game->pturn].team))
				{
					z = player[j / 100].unit[j % 100].type;
					if ((unitstats[z].movetype != M_INFANTRY) && (unitstats[z].movetype != M_MECH))
					{
						good[i] = true;
					}
				}
			}
			i++;
		}
		first = false;
	}

	i = 0;
	while (i < 4)
	{
		if (good[i])
		{
			x = (u->tilex - worldmap.camera.get_scroll_x()) * MAP_TILE_SIZE + worldmap.camera.get_offset_x();
			y = (u->tiley - worldmap.camera.get_scroll_y()) * MAP_TILE_SIZE + worldmap.camera.get_offset_y();

			if (i == 0) x += MAP_TILE_SIZE;
			if (i == 1) x -= MAP_TILE_SIZE;
			if (i == 2) y += MAP_TILE_SIZE;
			if (i == 3) y -= MAP_TILE_SIZE;

			buffer_rectfill(x, y, x + MAP_TILE_SIZE, y + MAP_TILE_SIZE, WHITE, 0.66);
		}
		i++;
	}

	if (mouse_clicked() && (mx < SCREEN_W - SIDEBAR_WIDTH) && (my < SCREEN_H - BOTTOMBAR_HEIGHT))
	{
		_tile *tile = worldmap.get_mouseover_tile(mx, my);
		
		if (tile == 0)
			return;
		
		if      (tile->x == u->tilex + 1) j = 0;
		else if (tile->x == u->tilex - 1) j = 1;
		else if (tile->y == u->tiley + 1) j = 2;
		else if (tile->y == u->tiley - 1) j = 3;
		else j = -1;

		if ((j != -1) && (good[j]))
		{
			if ((!global.netgame) || (global.netgame && net.server))
			{
				mechanic_capture(u, tile->x, tile->y);
				game->state = NOTHING_HAPPENING;

				if (global.netgame && net.server)
				{
					data.clear();
					data.appendByte(NET_MECHANIC_CAPTURE);
					data.appendByte(game->pturn);
					data.appendByte(u->number);
					data.appendByte(tile->x);
					data.appendByte(tile->y);
					net.engine->broadcastTCP(data);
				}
			}
			else
			{  //netgame client
				game->state = NET_WAITING_FOR_RESPONSE;
				data.clear();
				data.appendByte(NET_CLIENT_MECHCAP);
				data.appendByte(u->number);
				data.appendByte(tile->x);
				data.appendByte(tile->y);
				net.engine->sendTCP(net.thread, data);
			}

			game->unitselected = NULL;
			first = true;
		}
	}

	if (mouse_rclicked())
	{
		first = true;
		game->state = NOTHING_HAPPENING;
	}
	check_scrolling();
}

void mechanic_capture(_unit *capper, int captx, int capty)
{
	int z = any_unit_here(captx, capty);
	int p = capper->owner;
	int x, y, i;
	_unit *v = &player[z / 100].unit[z % 100];

	x = capper->tilex * MAP_TILE_SIZE + MAP_TILE_SIZE / 2;
	y = capper->tiley * MAP_TILE_SIZE + MAP_TILE_SIZE / 2;
	i = 0;
	while (i < 15)
	{
		create_particle(x + rand()%MAP_TILE_SIZE - MAP_TILE_SIZE / 2, y + rand()%MAP_TILE_SIZE - MAP_TILE_SIZE / 2, P_DUST);
		i++;
	}

	capper->exists = 0;
	capper->health = 0;
	worldmap.get_tile(capper->tilex, capper->tiley)->set_unit(-1, -1);

	i = player[p].create_unit(v->type, v->tilex, v->tiley, p);
	player[p].unit[i].health = v->health;
	player[p].unit[i].load[0] = v->load[0];
	player[p].unit[i].load[1] = v->load[1];
	v->exists = 0;
	v->health = 0;
	worldmap.get_tile(captx, capty)->set_unit(p, i);

	x = captx * MAP_TILE_SIZE + MAP_TILE_SIZE / 2;
	y = capty * MAP_TILE_SIZE + MAP_TILE_SIZE / 2;
	create_particle(x, y, P_QUADSPIRAL, player[p].tintcolor);
}


void unit_teleporting()
{
	static bool first = true;
	static bool selecting;
	static int destx, desty, time;
	static float a, inc;
	int t1, t2, t3, t4;
	int mx = mouse_x;
	int my = mouse_y;
	_unit *u = game->unitselected;
	DyBytes data;

	if (first)
	{
		selecting = true;
		destx = -1;
		desty = -1;
		a = 0.5;
		inc = 0.02;
		time = 0;
		first = false;
	}

	if (selecting)
	{
		t2 = worldmap.camera.get_scroll_y() - 1;
		if (t2 < 0) t2 = 0;
		while ((t2 < worldmap.camera.get_scroll_y() + YTILES + 1) && (t2 < worldmap.get_height()))
		{
			t1 = worldmap.camera.get_scroll_x() - 1;
			if (t1 < 0) t1 = 0;
			while ((t1 < worldmap.camera.get_scroll_x() + XTILES + 1) && (t1 < worldmap.get_length()))
			{
				if (worldmap.get_tile(t1, t2)->same_type(6, 0))
				{
					t3 = (t1 - worldmap.camera.get_scroll_x()) * MAP_TILE_SIZE;
					t4 = (t2 - worldmap.camera.get_scroll_y()) * MAP_TILE_SIZE;
					buffer_rect(t3 + worldmap.camera.get_offset_x(),
							    t4 + worldmap.camera.get_offset_y(),
							    t3 + MAP_TILE_SIZE + worldmap.camera.get_offset_x(),
							    t4 + MAP_TILE_SIZE + worldmap.camera.get_offset_y(),
							    WHITE, a);
				}
				t1++;
			}
			t2++;
		}
		a += inc;
		if (a >= 1.0)
		{
			a = 1.0;
			inc = -0.02;
		}
		if (a <= 0.25) inc = 0.02;

		buffer_textout_centre_ex(big, "Select Destination", 320, 0, WHITE, a);

		if (mouse_clicked() && (mx < SCREEN_W - SIDEBAR_WIDTH) && (my < SCREEN_H - BOTTOMBAR_HEIGHT))
		{
			_tile *t = worldmap.get_mouseover_tile(mouse_x, mouse_y);
			
			if (t != 0)
			{
				if ((t1 != u->tilex) || (t2 != u->tiley))
				{
					if (t->same_type(6, 0))  //if this tile is a teleporter
					{
						destx = t->x;
						desty = t->y;
						selecting = false;
					}
				}
			}
		}
		else if (mouse_rclicked())
		{
			first = true;
			game->state = UNIT_SELECTED;
		}
		check_scrolling();
	}
	else
	{  //teleportation animation
		if ((!global.netgame) || (global.netgame && net.server))
		{
			start_teleport_animation(u, destx, desty);

			if (global.netgame)
			{
				data.clear();
				data.appendByte(NET_TELEPORT);
				data.appendByte(game->pturn);
				data.appendByte(u->number);
				data.appendByte(destx);
				data.appendByte(desty);
				net.engine->broadcastTCP(data);
			}
		}
		else
		{  //client in a netgame
			data.clear();
			data.appendByte(NET_CLIENT_TELEPORT);
			data.appendByte(u->number);
			data.appendByte(destx);
			data.appendByte(desty);
			net.engine->sendTCP(net.thread, data);

			game->state = NET_WAITING_FOR_RESPONSE;
		}

		game->unitselected = NULL;
		first = true;
	}
}

void start_teleport_animation(_unit *u, int tx, int ty)
{
	teleport_animation(u, tx, ty);
	game->state = TELEPORT_ANIMATION;
}

void teleport_animation(_unit *initunit, int tx, int ty)
{
	static int time = 0, destx = -1, desty = -1;
	static _unit *u = NULL;
	int t1, t2, z;

	if (initunit != NULL)
	{  //if initializing the animation
		u = initunit;
		destx = tx;
		desty = ty;
		time = 0;
	}

	if (time == 0)
	{
		if (scroll_to_location(u->tilex, u->tiley) == 0)
		{
			time--;
		}
	}
	else if (time == 1)
	{
		t1 = u->tilex * MAP_TILE_SIZE;
		t2 = u->tiley * MAP_TILE_SIZE;
		create_particle(t1 + MAP_TILE_SIZE / 2, t2 + MAP_TILE_SIZE / 2, P_SHOCKWAVE);
		create_particle(t1, t2, P_TELEPORT);
		play_sound(S_TELEPORT);
	}
	else if (time == 9)
	{
		u->exists = 0;
	}
	else if (time == 25)
	{
		if (scroll_to_location(destx, desty) == 0)
		{
			time--;
		}
	}
	else if (time == 26)
	{
		z = any_unit_here(destx, desty);
		if (z != -1)
		{
			if (u->owner != z / 100) player[u->owner].stats.killed++;
			player[z / 100].stats.lost++;

			player[z / 100].unit[z % 100].kill();
		}
		if (check_explodes())
		{
			do_explodes();
			time--;
		}
		else
		{
			u->set_tiles(destx, desty);
			create_particle(destx * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, desty * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, P_SHOCKWAVE);
			create_particle(destx * MAP_TILE_SIZE, desty * MAP_TILE_SIZE, P_TELEPORT);
			play_sound(S_TELEPORT);
		}
	}
	else if (time == 34)
	{
		u->exists = 1;
		u->ready = 0;
	}
	else if (time > 50)
	{
		game->state = NOTHING_HAPPENING;
	}
	time++;
}

int money_producing_buildings_owned(int plyr)
{
	int num = 0;
	
	for (int y = 0; y < worldmap.get_height(); y++)
	{
		for (int x = 0; x < worldmap.get_length(); x++)
		{
			_tile *tile = worldmap.get_tile(x, y);
			
			if (tile->owned_by(plyr))
			{
				if ((tile->is_building(CITY)) || (tile->is_building(HQ)))
					num++;
			}
		}
	}
	return num;
}

int unit_here_thorough(int plyr, int tx, int ty, int ignore_n)
{
	int i = 0;
	_unit *u;
	while (i < 50)
	{
		if (i != ignore_n)
		{
			u = &player[plyr].unit[i];
			if (u->exists == 1)
			{
				if ((u->tilex == tx) && (u->tiley == ty))
				{
					return i;
				}
			}
		}
		i++;
	}
	return -1;
}

int rounded_health(float h)
{
	int whole = int(h);
	int part = int((h - float(whole)) * 10);
	if ((whole == 0) && (h > 0))
	{
		return 1;
	}
	else if (whole == 10)
	{
		return 10;
	}
	else
	{
		if (part >= 5)
		{
			return whole + 1;
		}
		else
		{
			return whole;
		}
	}
}

void transform_buildings(int plyr, int plyr2)
{
	for (int y = 0; y < worldmap.get_height(); y++)
	{
		for (int x = 0; x < worldmap.get_length(); x++)
		{
			_tile *t = worldmap.get_tile(x, y);
			if ((t->is_unit_producing()) || (t->is_HQ()))
			{
				if (t->owned_by(plyr))
				{
					if (t->is_HQ())
					{
						t->change_to_city();
						t->change_owner(plyr2);
					}
					else
					{  //building here
						t->change_owner(plyr2);
					}
				}
			}
		}
	}
	
	minimap.create();
}

void repair_units(int plyr)
{
	int btype;
	bool rep;
	float oldh;
	_unit *u;
	_tile *t;
	
	for (int i = 0; i < 50; i++)
	{
		u = &player[plyr].unit[i];
		if (u->exists == 1)
		{
			rep = false;
			t = worldmap.get_tile(u->tilex, u->tiley);
			
			if (t == 0)
			{
				char c[300];
				sprintf(c, "ERROR: In repair_units, get_tile() returned NULL for player %d unit %d (tile %d,%d)", plyr, i, u->tilex, u->tiley);
				empdebug(c);
				continue;
			}
			
			if (t->is_building())
			{
				if (t->owned_by(plyr))
				{
					btype = t->building_type();
					switch(unitstats[u->type].basetype)
					{
					case LAND:
						if ((btype == CITY) || (btype == HQ) || (btype == BASE))
						{
							rep = true;
						}
						break;
					case SEA:
						if (btype == PORT)
						{
							rep = true;
						}
						break;
					case AIR:
						if (btype == AIRPORT)
						{
							rep = true;
						}
						break;
					}
				}
			}
			if (rep == true)  //if the unit is on a friendly base
			{
				if (u->health < 10)  //if the unit is damaged, add 2 health but take some cash
				{
					oldh = u->health;
					u->health += 2;
					if (u->health > 10)
					{
						u->health = 10;
					}
					player[plyr].cash -= int((u->health - oldh) / 20 * u->price);
					player[plyr].stats.spent += int((u->health - oldh) / 20 * u->price);
					if (player[plyr].cash < 0)
					{
						player[plyr].cash = 0;
					}
				}
			}
		}
	}
}

void play_moving_sound(int utype)
{
	play_sound(unitstats[utype].movesound);
}

void init_explode(int tx, int ty)
{
	_unitexplosion u;
	
	u.exists = 1;
	u.tx = tx;
	u.ty = ty;
	u.frame = 0;
	u.notaunit_oilderrick = false;
	
	boom.push_back(u);
}

void init_deadoilderrick(int tx, int ty)
{
	_unitexplosion u;
	
	u.exists = 1;
	u.tx = tx;
	u.ty = ty;
	u.frame = 0;
	u.notaunit_oilderrick = true;
	
	boom.push_back(u);
}

void destroy_player_units(int theplayer)
{
	int i = 0;
	char str[20];

	i = 0;
	while (i < 50)
	{
		if (player[theplayer].unit[i].exists == 1)
			player[theplayer].unit[i].kill();

		i++;
	}

	sprintf(str, "Player %d Defeated", theplayer + 1);
	create_particle((global.screen_width - SIDEBAR_WIDTH) / 2 - normal.Width(str) / 2, global.screen_height / 2, P_FADETEXT, WHITE, str);
}

int do_explodes()
{
	static bool first = true;
	static bool scrolling;
	static int n, x, y;
	unsigned int i;

	if (first)
	{
		n = -1;
		i = 0;
		while (i < boom.size())
		{
			if (boom[i].exists == 1)  //find the first explosion on the list
			{
				n = i;
				i = boom.size();
			}
			i++;
		}
		if (n == -1) return 1;

		scrolling = true;
		first = false;
	}

	if (scrolling)
	{
		if (scroll_to_location(boom[n].tx, boom[n].ty) == 1)
		{  //if it has finished doing the smooth scroll to the explosion site
			scrolling = false;

			if (boom[n].notaunit_oilderrick)
			{
				x = boom[n].tx * MAP_TILE_SIZE + (MAP_TILE_SIZE / 2);
				y = boom[n].ty * MAP_TILE_SIZE + (MAP_TILE_SIZE / 2);
				i = 0;
				while (i < 15)
				{
					create_particle(x + rand()%MAP_TILE_SIZE - MAP_TILE_SIZE / 2, y + rand()%MAP_TILE_SIZE - MAP_TILE_SIZE / 2, P_DUST);
					i++;
				}
			}
			else
			{
				x = (boom[n].tx - worldmap.camera.get_scroll_x()) * MAP_TILE_SIZE + worldmap.camera.get_offset_x();
				y = (boom[n].ty - worldmap.camera.get_scroll_y()) * MAP_TILE_SIZE + worldmap.camera.get_offset_y();
				play_sound(S_EXPLOSION);
				worldmap.get_tile(boom[n].tx, boom[n].ty)->set_unit(-1, -1);
			}
		}
	}
	if (!scrolling)
	{
		boom[n].frame += 1.5;
		if (boom[n].notaunit_oilderrick)
		{
			create_particle(x + rand()%MAP_TILE_SIZE - MAP_TILE_SIZE / 2, y + rand()%MAP_TILE_SIZE - MAP_TILE_SIZE / 2, P_DUST);
			create_particle(x + rand()%MAP_TILE_SIZE - MAP_TILE_SIZE / 2, y + rand()%MAP_TILE_SIZE - MAP_TILE_SIZE / 2, P_DUST);
			if (int(boom[n].frame) > 30)
			{
				boom[n].exists = 0;
				first = true;
				return 1;
			}
		}
		else
		{
			if (int(boom[n].frame) > 29)
			{
				boom[n].exists = 0;
				first = true;
				return 1;
			}
			else
			{
				create_particle(boom[n].tx * MAP_TILE_SIZE + (MAP_TILE_SIZE / 2), boom[n].ty * MAP_TILE_SIZE + (MAP_TILE_SIZE / 2), P_FLAME);
				explode1.draw(int(boom[n].frame), x, y);
			}
		}
	}
	return 0;
}

int secure_base(int plyr)
{
	int x, y, u;
	y = 0;
	while (y < worldmap.get_height())
	{
		x = 0;
		while (x < worldmap.get_length())
		{
			_tile *tile = worldmap.get_tile(x, y);
			if (tile->is_unit_producing())
			{
				if (tile->owned_by(plyr))
				{  //if it belongs to the player
					u = tile->unit_here();
					if ((u == -1) || (u / 100 == plyr) || (player[u / 100].team == player[plyr].team))
					{  //there is no unit here or a unit that belongs to this player or an ally
						return 1;
					}
				}
			}
			x++;
		}
		y++;
	}
	return 0;
}

void capture_building(_unit* u)
{
	char str[20];
	int z;
	_tile *t = worldmap.get_tile(u->tilex, u->tiley);

	empdebug("Capturing building...");

	if (!((t->is_unit_producing()) || (t->is_HQ())))
	{  //can't do a capture action on a building that doesn't create units and isn't an HQ
		empdebug("Wait, this building can't be captured...  ERROR!!!");
		empdebug(u->tilex);
		empdebug(u->tiley);
		return;
	}

	if (t->is_HQ())
	{  //if a player's HQ was captured
		int particlex, particley;
		
		z = t->get_owner();
		transform_buildings(z, u->owner);  //captured player's buildings go to the capturer
		game->state = PLAYER_DEFEATED;
		destroy_player_units(z);
		player[z].playing = 0;
		sprintf(str, "HQ Captured");
		
		get_good_textpos(t, str, &particlex, &particley);
		
		create_particle(particlex, particley, P_FADETEXT, WHITE, str);
	}
	else
	{
		int particlex, particley;
		
		t->change_owner(u->owner);
		str[0] = '\0';
		switch (t->building_type())
		{
		case BASE:
			sprintf(str, "Factory Captured");
			break;
		case AIRPORT:
			sprintf(str, "Airfield Captured");
			break;
		case PORT:
			sprintf(str, "Shipyard Captured");
			break;
		}
		
		get_good_textpos(t, str, &particlex, &particley);
		
		create_particle(particlex, particley, P_FADETEXT, WHITE, str);
	}
	minimap.create();
	empdebug("Building capture function complete.");
}

bool can_merge(_unit *a, _unit *b)
{
	int numloaded = 0;
	if (a->type != b->type) return false;
	if (a->owner != b->owner) return false;
	if (a == b) return false;  //can't merge with itself
	if ((a->health < 10) || (b->health < 10))
	{
		if (a->load[0].loaded == 1)
		{
			numloaded++;
		}
		if (a->load[1].loaded == 1)
		{
			numloaded++;
		}
		if (b->load[0].loaded == 1)
		{
			numloaded++;
		}
		if (b->load[1].loaded == 1)
		{
			numloaded++;
		}
		if (numloaded <= unitstats[a->type].loadcapacity)
		{
			return true;
		}
	}
	return false;
}

void merge_units(_unit* merger, int tx, int ty)
{
	int u = unit_here_thorough(merger->owner, tx, ty, merger->number);
	_unit* mergee = &player[merger->owner].unit[u];
	merger->health += mergee->health;
	if (merger->health > 10)
	{
		player[merger->owner].cash += int(merger->price * (merger->health - 10.0) / 10.0);
		if (player[merger->owner].cash > 1000000) player[merger->owner].cash = 1000000;
		merger->health = 10;
	}
	if (mergee->load[0].loaded == 1)
	{
		if (merger->load[0].loaded == 0)
		{
			merger->load[0] = mergee->load[0];
		}
		else
		{
			merger->load[1] = mergee->load[0];
		}
	}
	if (mergee->load[1].loaded == 1)
	{
		if (merger->load[0].loaded == 0)
		{
			merger->load[0] = mergee->load[1];
		}
		else
		{
			merger->load[1] = mergee->load[1];
		}
	}
	if (mergee->ready == 0) merger->ready = 0;
	worldmap.get_tile(tx, ty)->set_unit(merger->owner, merger->number);
	mergee->exists = 0;
}

bool check_explodes()
{
	if ((game->state != EXPLODING) && (game->state != IN_BATTLE))
	{  //if it's not in the middle of an explosion animation
		for (unsigned int i = 0; i < boom.size(); i++)
		{
			if (boom[i].exists == 1)  //check to see if it should be
				return true;
		}
		
		//if there aren't any existing explosions left
		boom.resize(0);  //get rid of all the finished explosions
	}
	
	return false;
}

void update_cities()
{
	for (int y = 0; y < worldmap.get_height(); y++)
	{
		for (int x = 0; x < worldmap.get_length(); x++)
		{
			_tile *t = worldmap.get_tile(x, y);
			if ((t->is_building()) && (!(t->is_unit_producing())) && (!(t->is_HQ())))
			{  //if it's an oil derrick
				int z = t->unit_here();
				if (z == -1)
				{
					t->change_owner(-1);
				}
				else if (unitstats[player[z / 100].unit[z % 100].type].cancapture)
				{
					if (!(t->owned_by(z / 100)))
					{
						int particlex, particley;
						char str[] = "Derrick Held";
						
						t->change_owner(z / 100);
						
						get_good_textpos(t, str, &particlex, &particley);
						create_particle(particlex, particley, P_FADETEXT, WHITE, str);
					}
				}
			}
		}
	}
	
	minimap.create();
}

void update_burning()
{
	const int d_direct = 75, d_adjacent = 15;

	for (int j = 0; j < worldmap.get_height(); j++)
	{
		for (int i = 0; i < worldmap.get_length(); i++)
		{
			if (worldmap.get_tile(i, j)->burning)
			{
				_tile *burning_tile = worldmap.get_tile(i, j);
				int z = burning_tile->unit_here();
				_unit *u = 0;
				
				if (z != -1)
				{
					u = &player[z / 100].unit[z % 100];
					if (u->exists == 1)
						do_firedamage(u, d_direct);
				}

				for (int k = 0; k < 4; k++)
				{
					int x = i;
					int y = j;

					if (k == 0) x--;
					if (k == 1) x++;
					if (k == 2) y--;
					if (k == 3) y++;

					if ((x > -1) && (x < worldmap.get_length()) && (y > -1) && (y < worldmap.get_height()))
					{
						_tile *adjacent_tile = worldmap.get_tile(x, y);
						
						z = adjacent_tile->unit_here();
						if (z != -1)
						{
							u = &player[z / 100].unit[z % 100];
							if (u->exists == 1)
								do_firedamage(u, d_adjacent);
						}
						if (burning_tile->burnedfor > 0)
						{  //if this forest wasn't just set on fire during this loop
							if ((!adjacent_tile->burning) && (adjacent_tile->same_type(0, 2)))
							{  //if there's an adjacent unburned forest
								if (rand()%4 == 0)
								{  //1 in 4 chance of it catching on fire
									adjacent_tile->burning = true;
									adjacent_tile->burnedfor = 0;
								}
							}
						}
					}
				}
				
				burning_tile->burnedfor++;
				if (burning_tile->burnedfor > 4 + rand()%4)
				{  //eventually burn out
					burning_tile->burning = false;
					burning_tile->burnedfor = -1;
					burning_tile->change_type(0, 20);
				}
			}
		}
	}
}

void do_firedamage(_unit *u, int damage)
{
	int d = 0;

	if (unitstats[u->type].basetype == AIR) return;

	if (damage - u->resist[D_FIRE] > 0)
	{
		d = damage - u->resist[D_FIRE];
	}
	else
	{
		d = int(10.0 * float(damage) / float(u->resist[D_FIRE]));
	}
	u->health -= d / 10.0;

	if (global.netgame)
	{
		if (net.server)
		{
			broadcast_unithealth(u);
		}
	}

	if (u->health < 0.01)
	{
		u->kill();
		player[u->owner].stats.lost++;
	}
}

void debug_savegame()
{
	if (!save_game_to_file("Debuggery", "saves/gamedebug.sav"))
		empdebug("Error creating debug save file");
}

bool sync_burning()
{
	int i;
	DyBytes data;

	if (net.server)
	{
		update_burning();
		broadcast_tiledata();

		data.clear();
		data.appendByte(NET_FINISHEDSYNC);
		net.engine->broadcastTCP(data);
		return true;
	}
	else  //if this is a client machine
	{
		if (get_host_message(data))
		{
			i = data.getByte(0);
			data.removeFront(1);
			switch(i)
			{
			case NET_UPDATINGTILES:
				get_tiledata(&data);
				break;
			case NET_UNITHEALTH:
				get_unithealth(&data);
				break;
			case NET_FINISHEDSYNC:
				return true;
				break;
			}
		}
	}
	return false;
}

void broadcast_unithealth(_unit *u)
{
	DyBytes data;

	data.clear();
	data.appendByte(NET_UNITHEALTH);
	data.appendByte(u->tilex);
	data.appendByte(u->tiley);
	data.appendVariable(u->health);

	net.engine->broadcastTCP(data);
}

void get_unithealth(DyBytes *data)
{
	int p = data->getByte(0), q = data->getByte(1);
	int z = any_unit_here(p, q);
	_unit *u;

	data->removeFront(2);

	if (z == -1)
	{
		empdebug("NET ERROR: Received unit health, but no unit on that tile!");
		empdebug(p);
		empdebug(q);
		return;
	}

	u = &player[z / 100].unit[z % 100];
	data->extractVariable(0, u->health);

	if (u->health < 0.01)
	{
		u->kill();
		player[u->owner].stats.lost++;
	}
}

void broadcast_tiledata()
{
	int x, y;
	_tile *t;
	DyBytes data, tiledata;

	tiledata.clear();
	y = 0;
	while (y < worldmap.get_height())
	{
		x = 0;
		while (x < worldmap.get_length())
		{
			t = worldmap.get_tile(x, y);
			tiledata.appendByte(t->burning);
			tiledata.appendByte(t->burnedfor);
			tiledata.appendByte(t->get_set());
			tiledata.appendByte(t->get_number());
			tiledata.appendByte(t->get_owner());
			x++;
		}
		y++;
	}
	tiledata.compress(9);
	data.clear();
	data.appendByte(NET_UPDATINGTILES);
	data.append(tiledata);
	net.engine->broadcastTCP(data);
}

void get_tiledata(DyBytes *data)
{
	int x, y;
	_tile *t;

	data->decompress();
	y = 0;
	while (y < worldmap.get_height())
	{
		x = 0;
		while (x < worldmap.get_length())
		{
			t = worldmap.get_tile(x, y);
			
			t->burning = data->getByte(0);
			if (t->burning != 0)
				t->burnedfor = data->getByte(1);
			else
				t->burnedfor = -1;
			
			if (!t->same_type(data->getByte(2), data->getByte(3)))
			{
				t->change_type(data->getByte(2), data->getByte(3));
			}
			t->change_owner(data->getByte(4));
			data->removeFront(5);

			x++;
		}
		y++;
	}
}

void draw_net_icon(int x, int y)
{
	static float a = 0.0;
	static float av = 0.04;

	a += av;
	if (a >= 1.0)
	{
		a = 1.0;
		av = -av;
	}
	else if (a <= 0.0)
	{
		a = 0.0;
		av = -av;
	}

	buffer_draw_sprite(glb.bmp(41), x, y, a);
}

void draw_wait_icon(int x, int y)
{
	static float a = 0.0;
	static float av = 0.01;

	a += av;
	if (a >= 0.75)
	{
		a = 0.75;
		av = -av;
	}
	else if (a <= 0.0)
	{
		a = 0.0;
		av = -av;
	}

	buffer_draw_sprite(glb.bmp(50), x, y, a);
}

void draw_shieldrange(_unit *u)
{
	int i, j, x, y;
	static float alpha = 0.3;
	static float alphachange = 0.007;

	j = u->tiley - SHIELD_RADIUS;
	if (j < 0) j = 0;
	while ((j <= u->tiley + SHIELD_RADIUS) && (j < worldmap.get_height()))
	{
		i = u->tilex - SHIELD_RADIUS;
		if (i < 0) i = 0;
		while ((i <= u->tilex + SHIELD_RADIUS) && (i < worldmap.get_length()))
		{
			if (tile_distance(i, j, u->tilex, u->tiley) <= SHIELD_RADIUS)
			{
				if ((i != u->tilex) || (j != u->tiley))
				{
					x = (i - worldmap.camera.get_scroll_x()) * MAP_TILE_SIZE + worldmap.camera.get_offset_x();
					y = (j - worldmap.camera.get_scroll_y()) * MAP_TILE_SIZE + worldmap.camera.get_offset_y();
					buffer_rectfill(x, y, x + MAP_TILE_SIZE, y + MAP_TILE_SIZE, BLUE, alpha);
				}
			}
			i++;
		}
		j++;
	}
	alpha += alphachange;
	if ((alpha >= 0.66) || (alpha <= 0.3)) alphachange *= -1;
}

bool help_screen()
{
	glb.draw_fullscreen(53);
	return mouse_clicked();
}

void do_surrender(int from, int to)
{
	stringstream s;
	bool explosions = false;
	
	if (to == -1)
	{
		destroy_player_units(from);
		explosions = true;
	}
	else
	{
		explosions = !convert_theplayers_units(from, to);  //if convert_theplayers_units can't convert all units, the rest explode
	}

	transform_buildings(from, to);
	player[from].playing = 0;
	
	s << "Player " << from << " surrendering to " << to;
	empdebug(s.str());

	if ((game->pturn == from) && !explosions)
		game->next_turn();  //only go straight to the next turn if there aren't any unit explosions to animate first
}

bool convert_theplayers_units(int from, int to)
{
	bool full = false;
	int a, i = 0;
	char str[30];
	_unit *u, *v;

	while ((i < 50) && !full)
	{
		u = &player[from].unit[i];
		if (u->exists == 1)
		{
			a = player[to].create_unit(u->type, u->tilex, u->tiley, to);
			if (a == -1)
			{
				full = true;
				i--;
			}
			else
			{
				v = &player[to].unit[a];
				v->health = u->health;
				v->load[0] = u->load[0];
				v->load[1] = u->load[1];
				u->exists = 0;
				u->health = 0;
				worldmap.get_tile(v->tilex, v->tiley)->set_unit(to, a);
			}
		}
		i++;
	}
	while (i < 50)
	{
		u = &player[from].unit[i];
		if (u->exists == 1)
		{
			player[to].cash += int(float(u->price) * (u->health / 10.0));
			u->kill();
		}
		i++;
	}

	sprintf(str, "Player %d Defeated", from + 1);
	create_particle((global.screen_width - SIDEBAR_WIDTH) / 2 - normal.Width(str) / 2, global.screen_height / 2, P_FADETEXT, WHITE, str);
	
	return !full;  //return false if not all units could be converted
}

void get_good_textpos(_tile *t, const string &str, int *px, int *py)
{
	const int width = big.Width(str);
	const int screentx = t->x - worldmap.camera.get_scroll_x();
	const int screenty = t->y - worldmap.camera.get_scroll_y();
	
	if (screenty < YTILES - 2)  //if the tile is not near the bottom of the screen, put the message right below the tile
		*py = (screenty + 1) * MAP_TILE_SIZE + worldmap.camera.get_offset_y();
	else  //otherwise put it above
		*py = screenty * MAP_TILE_SIZE - big.Height(str);
	
	*px = screentx * MAP_TILE_SIZE + (MAP_TILE_SIZE / 2) - (width / 2);
	
	if (*px < 10)
		*px = 10;
	if (*px + width > XTILES * MAP_TILE_SIZE - 10)
		*px = XTILES * MAP_TILE_SIZE - width - 10;
}
