#include "empire.h"
#include "netlobby.h"

extern void get_teleports();
extern void get_connected_beaches();
extern void draw_minimap(int x, int y, string file);
extern void load_army_settings();
extern void load_unit_settings();

void send_lobbydata(string mapname);
void send_inidata(int thread);

void broadcast_unreadyall();

void send_playerdata(int thread, int placement);
bool load_client_playerdata(int p, DyBytes *data);

DyBytes menudata();
void send_menudata(int thread);
void broadcast_menudata();

void disconnect(int thread);
void do_netai_options(_button *b, int *colors, _button *ccycle, _button *pcycle, _button *tcycle);
void remove_player(int j, int *colors, _button *ccyc, _button *pcyc, _button *tcyc);
void net_assign_tintcolor(int p, int *colors);

void cycle_startcash(int amount, _button &startingcash);
void cycle_funding(int amount, _button &fundsperbuilding);
void cycle_techlevel(int amount, _button &techlevel);
void cycle_cashturns(int amount, _button &cashturns);

typedef struct hostclient
{
	bool maploaded;
	bool isai;
	bool ready;
	short baddata;
	
	hostclient()
	{
		maploaded = false;
		isai = false;
		ready = false;
		baddata = 0;
	}
} hostclient;

vector<hostclient> clients;

int net_hostgame()
{
	static bool first = true, changemap = false, returning_to_lobby = false;
	static int oldsel, prevmap, colors[4], last_lobby_update;
	static _button team, plyrcycle[4], teamcycle[4], fundsperbuilding, showdamage, startingcash, cashturns,
									back, mapchange, mapok, mapcancel, start, techlevel, colorcycle[4], boot[3], ai[3];
	static _listbox chatbox, mapbox;
	static _textline chattext;
	bool notnumplayers = true;
	bool allready = false;
	bool pcycleclicked[4], pcyclerclicked[4], tcycleclicked[4], tcyclerclicked[4], ccycleclicked[4], ccyclerclicked[4];
	char c[70];
	string str, s;
	DyBytes data;
	
	if (first)
	{
		play_song(battlemenu_music);
		load_army_settings();  //this machine may have been a network client previously
		load_unit_settings();  //so make sure the unit and army settings are the local ones
		
		last_lobby_update = 5;
		returning_to_lobby = false;
		
		gameoptions.funding = 4000;
		gameoptions.show_damage = true;
		gameoptions.time = -999;
		oldsel = -1;
		changemap = false;
		
		clients.clear();
		
		for (int i = 0; i < 4; i++)
		{
			currentcharacter[i] = character[0];
			player[i].number = 0;
			player[i].team = i + 1;
			player[i].tintcolor = player_color(0, i);
			colors[i] = i;
			
			plyrcycle[i].init(44, 42 + 116 * i, 80, 80, "", character[0]->picture);
			teamcycle[i].init(150, 70 + 116 * i, 40, 40, "", glb.bmp(13 + player[i].team));
			colorcycle[i].init(275, 28 + 116 * i, 40, 40, "", NULL, 2, player[i].tintcolor);
		}
		
		mapchange.init(348, 529, 170, 40, "Change map");
		mapbox.init(348, 410, 170, 160);
		mapbox.setsel(0);
		prevmap = 0;
		
		for (int i = 0; i < signed(maplist.size()); i++)
		{
			mapbox.additem(maplist[i].c_str());
			
			if (maplist[i] == global.lastmap)
			{
				mapbox.setsel(i);
				prevmap = i;
			}
		}
		
		if (mapbox.size() > 0)
		{
			string s = mapbox.selected();
			s.erase(0, 5); //get rid of the (2P) / (3P) / (4P)
			load_ai(s);
		}

		mapok.init(348, 580, 80, 16, "OK");
		mapcancel.init(438, 580, 80, 16, "Cancel");
		changemap = false;
		
		back.init(10, 575, 100, 20, "Back");
		start.init(700, 570, 100, 25, "Start!", 1, BLACK);
		
		startingcash.init(348, 410, 170, 20, "Starting Cash: 0");
		fundsperbuilding.init(348, 431, 170, 20, "Funds per Building: 4000");
		showdamage.init(348, 452, 170, 20, "Show Attacking Damage: Yes");
		techlevel.init(348, 473, 170, 20, "Tech Level: 3");
		game->techlevel = 3;
		cashturns.init(348, 494, 170, 20, "Derricks Last: 10 Turns");
		game->cashturns = 10;
		
		net.clientthread[0] = -1;
		net.clientthread[1] = -1;
		net.clientthread[2] = -1;
		player[0].name = global.netname;
		player[1].name = "";
		player[2].name = "";
		player[3].name = "";
		chatbox.init(348, 39, 420, 321);
		chattext.init(348, 362, 420, 13, true);
		
		for (int i = 0; i <= 2; i++)
		{
			boot[i].init(275, 208 + (116 * i), 65, 20, "Kick!");
			ai[i].init(275, 208 + (116 * i), 65, 20, "Toggle AI");
		}
		
		first = false;
	}
	
	if (returning_to_lobby)
	{  //we've told any connected clients to disconnect, give them some time to do so
		static int delay = 0;
		bool none_connected = true;
		
		glb.draw(54, 0, 0);
		buffer_textout_centre_ex(big, "Ending hosted game...", 400, 200, WHITE);
		
		for (unsigned int i = 0; i < net.engine->getMaxConnections(); i++)
		{
			if (net.engine->isConnected(i) || net.chatengine->isConnected(i))
				none_connected = false;
		}
		
		delay++;
		if ((delay > 50) || none_connected)
		{
			if (!none_connected)
			{  //we've given them plenty of time, now kick them out
				for (unsigned int i = 0; i < net.engine->getMaxConnections(); i++)
					disconnect(i);
			}
			
			delay = 0;
			first = true;
			return NET_FIRST;
		}
		
		return NET_HOST;
	}
	
	for (int i = 0; i < 4; i++)
	{
		if (plyrcycle[i].clicked()) pcycleclicked[i] = true;
		else pcycleclicked[i] = false;
		
		if (plyrcycle[i].rclicked()) pcyclerclicked[i] = true;
		else pcyclerclicked[i] = false;
		
		if (teamcycle[i].clicked()) tcycleclicked[i] = true;
		else tcycleclicked[i] = false;
		
		if (teamcycle[i].rclicked()) tcyclerclicked[i] = true;
		else tcyclerclicked[i] = false;
		
		if (colorcycle[i].clicked()) ccycleclicked[i] = true;
		else ccycleclicked[i] = false;
		
		if (colorcycle[i].rclicked()) ccyclerclicked[i] = true;
		else ccyclerclicked[i] = false;
	}
	
	glb.draw(36, 0, 0);
	back.check();
	chatbox.check(false);
	str = chattext.check();
	buffer_textout_ex(normal, global.netname, 145, 34, BLACK);
	
	if (oldsel != mapbox.getsel())
	{
		s = mapbox.selected();
		s.erase(0, 5); //get rid of the (2P) / (3P) / (4P)
		sprintf(c, "maps/%s.map", s.c_str());
		load_map(mapbox.selected(), c, false);
		minimap.create();
		minimap.a = 1.0;
		oldsel = mapbox.getsel();
	}
	
	buffer_textout_ex(normal, worldmap.get_name(), 620, 384, BLACK);
	sprintf(c, "%d Players", numplayers);
	buffer_textout_ex(normal, c, 620, 397, BLACK);
	minimap.draw(false, 619, 412, false, true);
	
	buffer_rect(start.x - 6, start.y - 3, start.x + start.w + 2, start.y + start.h + 3, BLACK);
	buffer_rect(start.x - 3, start.y, start.x + start.w - 1, start.y + start.h, BLACK);
	
	if (changemap)
	{
		mapbox.check();
		mapok.check();
		mapcancel.check();
		if (mapok.clicked())
		{
			string s = mapbox.selected();
			s.erase(0, 5); //get rid of the (2P) / (3P) / (4P)
			load_ai(s);
			
			for (vector<hostclient>::iterator it = clients.begin(); it != clients.end(); it++)
			{
				it->maploaded = false;
				it->ready = false;
			}
			
			prevmap = mapbox.getsel();
			changemap = false;
			broadcast_unreadyall();
			broadcast_mapdata();
		}
		if (mapcancel.clicked())
		{
			mapbox.setsel(prevmap);
			changemap = false;
		}
	}
	else
	{
		int totalplayers = clients.size() + 1;
		notnumplayers = false;
		
		if (totalplayers < numplayers)
		{
			buffer_textout_ex(normal, "Not enough players for this map.", 348, 515, RED);
			notnumplayers = true;
		}
		else if (totalplayers > numplayers)
		{
			notnumplayers = true;
			buffer_textout_ex(normal, "Too many players for this map.", 348, 515, RED);
		}
		
		allready = true;
		for (vector<hostclient>::iterator it = clients.begin(); it != clients.end(); it++)
			if (!it->ready) allready = false;
		
		if (!notnumplayers && allready)
		{
			start.check();
		}
		
		mapchange.check();
		startingcash.check();
		fundsperbuilding.check();
		showdamage.check();
		//timelimit.check();
		techlevel.check();
		cashturns.check();
		if (mapchange.clicked())
		{
			changemap = true;
		}
	}
	
	do_netai_options(ai, colors, colorcycle, plyrcycle, teamcycle);
	
	do_hostchat(&chatbox, str);
	
	for (unsigned int i = 0; i < net.engine->getMaxConnections(); i++)
	{
		bool newclient = false;

		if (net.engine->acceptClient(i))
			newclient = true;
		net.chatengine->acceptClient(i);
		
		if (net.engine->isConnected(i))
		{
			if (newclient)
			{  //a new client has connected, send data to it
				
				if (clients.size() >= 3)
				{  //if we don't have room for this new connection
					disconnect(i);
				}
				else  //if this thread wasn't immediately disconnected
				{
					DyBytes askdata;
					askdata.appendByte(NET_ASK_PLAYERDATA);
					net.engine->sendTCP(i, askdata);
					
					broadcast_unreadyall();
					
					clients.push_back(hostclient());
					int c = clients.size() - 1;
					int pnum = c + 1;
					
					empdebug("New client");
					empdebug(c);
					empdebug("has connected on thread");
					empdebug(signed(i));
					
					player[pnum].name = "";
					net.address[pnum] = net.engine->getRemoteAddr(i);
					net.clientthread[c] = i;
					
					player[pnum].playing = 1;
					player[pnum].everplaying = 1;
					player[pnum].number = 0;
					player[pnum].controller = NET;
					player[pnum].team = pnum + 1;
					currentcharacter[pnum] = character[0];
					colors[pnum] = 0;
					net_assign_tintcolor(pnum, colors);
					
					plyrcycle[pnum].picture = currentcharacter[pnum]->picture;  //make sure the player, team, and tint buttons are set correctly
					teamcycle[pnum].picture = glb.bmp(13 + player[pnum].team);
					colorcycle[pnum].color = player[pnum].tintcolor;
					
					send_inidata(i);  //send the hosts's ini file settings
					send_menudata(i); //and the current game settings
					send_mapdata(i);  //and the map
				}
			}
		}
		else  //if the thread isn't connected, check if a client was using that thread
		{
			for (int j = 0; j < 3; j++)
			{
				if (net.clientthread[j] == signed(i))
				{
					empdebug("Client");
					empdebug(j);
					empdebug("on thread");
					empdebug(net.clientthread[j]);
					empdebug("is no longer connected.");
					
					net.clientthread[j] = -1;
					
					sprintf(c, "Player %s has left the game (Player %d disconnected)", player[j + 1].name.c_str(), j + 2);
					broadcast_chat_message(c);
					chatbox.additem(c);
					chatbox.scroll_to_end();
					
					data.clear();
					data.appendByte(NET_PLAYER_UPDATE);
					net.engine->broadcastTCP(data);
					
					broadcast_unreadyall();
					remove_player(j, colors, colorcycle, plyrcycle, teamcycle);
				}
			}
		}
	}
	
	for (unsigned int j = 0; j < 3; j++)
	{
		if (j >= clients.size())  //no more clients, break out of the loop
			break;
		
		hostclient &client = clients[j];
		int pnum = j + 1;
		
		if (player[pnum].name != "")
		{  //if we have this player's name
			buffer_rectfill(36, 36 + (116 * pnum), 127, 127 + (116 * pnum), WHITE);
			if (currentcharacter[pnum] != NULL)
			{
				buffer_draw_sprite(currentcharacter[pnum]->picture, 44, 157 + (116 * j));
				buffer_rectfill(275, 28 + (116 * pnum), 315, 68 + (116 * pnum), player[pnum].tintcolor);
			}
			else buffer_draw_sprite(glb.bmp(51), 44, 157 + 116 * j);
			
			buffer_draw_sprite(glb.bmp(13 + player[pnum].team), 150, 185 + (116 * j));
			
			buffer_textout_ex(normal, player[pnum].name, 145, 149 + (116 * j), BLACK);
			
			if (client.ready)
			{
				buffer_draw_sprite(glb.bmp(28), 275, 188 + (116 * j));
			}
		}
		else if (!client.isai)
		{
			buffer_rectrounded(29, 27 + (116 * pnum), 270, 133 + (116 * pnum), BLACK, 0.7, 3.0);
			buffer_textout_ex(big, "Getting Player Data...", 51, 65 + (116 * pnum), WHITE);
		}
		
		if (client.isai)
			continue;  //everything further down this loop assumes there's a remote connection for this client (so, not AI)
		
		boot[j].check();
		if (boot[j].clicked())
		{
			disconnect(net.clientthread[j]);
			
			sprintf(c, "%s has been shown the door (Player %d kicked)", player[j + 1].name.c_str(), j + 2);
			broadcast_chat_message(c);
			chatbox.additem(c);
			chatbox.scroll_to_end();
			
			continue;
		}

		if (get_client_message(net.clientthread[j], data))
		{
			
			uint8_t id = data.getByte(0);
			data.removeFront(1);
			
			empdebug("Host received data");
			
			if (id != NET_PLAYERDATA)
			{  //if the client isn't sending player data right now
				if (player[pnum].name == "")
				{  //and if we're still waiting for the client's name, ask for it again
					DyBytes askdata;
					askdata.appendByte(NET_ASK_PLAYERDATA);
					net.engine->sendTCP(net.clientthread[j], askdata);
				}
			}
			
			switch(id)
			{
				case NET_PLAYERDATA:
					empdebug("  NET_PLAYERDATA");
					data.decompress();
					if (!load_client_playerdata(pnum, &data))
					{  //if this client gave us bad data
						client.baddata++;
						if (client.baddata > 2)
						{  //three strikes and you're kicked
							disconnect(net.clientthread[j]);
						}
						data.clear();
						data.appendByte(NET_ASK_PLAYERDATA);
						net.engine->sendTCP(j, data);
						continue;
						break;
					}
					
					data.clear();
					data.appendByte(NET_PLAYER_UPDATE);
					net.engine->broadcastTCP(data);
					
					for (int k = 0; k < 3; k++)
						if (clientconnected(k))
						{
							send_playerdata(net.clientthread[k], k + 1);
						}
					break;
				case NET_CHARACTER:
					empdebug("  NET_CHARACTER");
					data.removeFront(1);  //remove playernum
					
					player[pnum].number++;
					if (unsigned(player[pnum].number) >= character.size()) player[pnum].number = -1;
					
					if (player[pnum].number != -1)
					{
						currentcharacter[pnum] = character[player[pnum].number];
						plyrcycle[pnum].picture = currentcharacter[pnum]->picture;
					}
					else
					{
						currentcharacter[pnum] = NULL;
						plyrcycle[pnum].picture = glb.bmp(51);
					}
					
					broadcast_unreadyall();
					data.clear();
					data.appendByte(NET_CHARACTER);
					data.appendByte(pnum);
					data.appendByte(player[pnum].number);
					net.engine->broadcastTCP(data);
					
					if (currentcharacter[pnum] == NULL)
					{
						colors[pnum] = -1;
						player[pnum].tintcolor = WHITE;
					}
					else
					{
						colors[pnum] = 0;
						net_assign_tintcolor(pnum, colors);
					}
					colorcycle[pnum].color = player[pnum].tintcolor;
					
					data.clear();
					data.appendByte(NET_PLAYERTINT);
					data.appendByte(pnum);
					data.appendVariable(player[pnum].tintcolor);
					net.engine->broadcastTCP(data);
					
					minimap.create();  //update the minimap to reflect the new player color
					minimap.a = 1.0;
					
					break;
				case NET_CLIENT_TINTCYCLE:
					empdebug("  NET_CLIENT_TINTCYCLE");
					
					if (currentcharacter[pnum] != NULL)
					{  //ignore if this client has a random character
						colors[pnum]++;
						net_assign_tintcolor(pnum, colors);
						
						colorcycle[pnum].color = player[pnum].tintcolor;
						
						data.clear();
						data.appendByte(NET_PLAYERTINT);
						data.appendByte(pnum);
						data.appendVariable(player[pnum].tintcolor);
						net.engine->broadcastTCP(data);
						
						minimap.create();  //update the minimap to reflect the new player color
						minimap.a = 1.0;
					}
					break;
				case NET_PLAYERTEAM:
					empdebug("  NET_PLAYERTEAM");
					data.removeFront(1);  //remove playernum
					
					player[pnum].team++;
					if (player[pnum].team > 4) player[pnum].team = 1;
					
					teamcycle[pnum].picture = glb.bmp(13 + player[pnum].team);
					
					broadcast_unreadyall();
					data.clear();
					data.appendByte(NET_PLAYERTEAM);
					data.appendByte(pnum);
					data.appendByte(player[pnum].team);
					net.engine->broadcastTCP(data);
					break;
				case NET_MAPLOADED:
					empdebug("  NET_MAPLOADED");
					client.maploaded = true;
					break;
				case NET_CLIENTREADY:
					empdebug("  NET_CLIENTREADY");
					client.ready = data.getByte(0);
					data.clear();
					data.appendByte(NET_CLIENTREADY);
					data.appendByte(pnum);
					data.appendByte(client.ready);
					net.engine->broadcastTCP(data);
					break;
			}
		}
	}
	
	for (unsigned int i = 0; i < clients.size() + 1; i++)
	{
		if (player[i].name != "")
		{
			buffer_rectfill(36, 36 + 116 * i, 127, 127 + 116 * i, WHITE);
			plyrcycle[i].check();
			buffer_rect(38, 36 + 116 * i, 128, 126 + 116 * i, BLACK);
			buffer_textout_ex(med, "Team", 156, 55 + 116 * i, WHITE);
			buffer_textout_ex(med, "Team", 158, 55 + 116 * i, WHITE);
			buffer_textout_ex(med, "Team", 156, 57 + 116 * i, WHITE);
			buffer_textout_ex(med, "Team", 158, 57 + 116 * i, WHITE);
			buffer_textout_ex(med, "Team", 157, 56 + 116 * i, BLACK);
			teamcycle[i].check();
			if (currentcharacter[i] != NULL) colorcycle[i].check();
		}
		
		if (pcycleclicked[i] || pcyclerclicked[i])
		{
			if (pcycleclicked[i]) player[i].number++;
			if (pcyclerclicked[i]) player[i].number--;
			
			if (player[i].number >= signed(character.size())) player[i].number = -1;
			if (player[i].number < -1) player[i].number = character.size() - 1;
			
			if (player[i].number != -1)
			{
				currentcharacter[i] = character[player[i].number];
				plyrcycle[i].picture = currentcharacter[i]->picture;
			}
			else
			{
				currentcharacter[i] = NULL;
				plyrcycle[i].picture = glb.bmp(51);
			}
			
			broadcast_unreadyall();
			
			data.clear();
			data.appendByte(NET_CHARACTER);
			data.appendByte(i);
			data.appendByte(player[i].number);
			net.engine->broadcastTCP(data);
			
			if (currentcharacter[i] != NULL)
			{
				colors[i] = 0;
				net_assign_tintcolor(i, colors);
			}
			else player[i].tintcolor = WHITE;
			colorcycle[i].color = player[i].tintcolor;
			
			minimap.create();  //update the minimap to reflect the new player color
			minimap.a = 1.0;
			
			data.clear();
			data.appendByte(NET_PLAYERTINT);
			data.appendByte(i);
			data.appendVariable(player[i].tintcolor);
			net.engine->broadcastTCP(data);
		}
		
		if (tcycleclicked[i] || tcyclerclicked[i])
		{
			if (tcycleclicked[i]) player[i].team++;
			if (tcyclerclicked[i]) player[i].team--;
			
			if (player[i].team > 4) player[i].team = 1;
			if (player[i].team < 1) player[i].team = 4;
			
			teamcycle[i].picture = glb.bmp(13 + player[i].team);
			broadcast_unreadyall();
			
			data.clear();
			data.appendByte(NET_PLAYERTEAM);
			data.appendByte(i);
			data.appendByte(player[i].team);
			net.engine->broadcastTCP(data);
		}
		
		if ((currentcharacter[i] != NULL) && (ccycleclicked[i] || ccyclerclicked[i]))
		{
			if (ccycleclicked[i]) colors[i]++;
			if (ccyclerclicked[i]) colors[i]--;
			
			if (colors[i] < 0) colors[i] = 3;
			
			net_assign_tintcolor(i, colors);
			colorcycle[i].color = player[i].tintcolor;
			
			minimap.create();  //update the minimap to reflect the new player color
			minimap.a = 1.0;
			
			data.clear();
			data.appendByte(NET_PLAYERTINT);
			data.appendByte(i);
			data.appendVariable(player[i].tintcolor);
			net.engine->broadcastTCP(data);
		}
	}
	
	if (!changemap)
	{
		if (startingcash.clicked())  cycle_startcash(5000, startingcash);
		if (startingcash.rclicked()) cycle_startcash(-5000, startingcash);
		
		if (fundsperbuilding.clicked())  cycle_funding(1000, fundsperbuilding);
		if (fundsperbuilding.rclicked()) cycle_funding(-1000, fundsperbuilding);
		
		if (showdamage.clicked() || showdamage.rclicked())
		{
			if (gameoptions.show_damage)
			{
				gameoptions.show_damage = false;
				showdamage.init(348, 452, 170, 20, "Show Attacking Damage: No");
			}
			else
			{
				gameoptions.show_damage = true;
				showdamage.init(348, 452, 170, 20, "Show Attacking Damage: Yes");
			}
			broadcast_unreadyall();
			
			data.clear();
			data.appendByte(NET_SHOWDAMAGE);
			data.appendByte(gameoptions.show_damage);
			net.engine->broadcastTCP(data);
		}
		
		if (techlevel.clicked())  cycle_techlevel(1, techlevel);
		if (techlevel.rclicked()) cycle_techlevel(-1, techlevel);
		
		if (cashturns.clicked())  cycle_cashturns(5, cashturns);
		if (cashturns.rclicked()) cycle_cashturns(-5, cashturns);
		
		if (!notnumplayers && allready && start.clicked())
		{  //if the start button was clicked and everything's ready to go
			global.lastmap = mapbox.selected();
			
			net.engine->disableTCPListen();  //don't allow anyone else to connect
			net.chatengine->disableTCPListen();
			
			//starting a game, so the lobby connection is no longer needed
			net.lobbyengine->disconnectHost(net.lobbythread);
			
			player[0].controller = HUMAN;
			player[0].name = global.netname;
			
			if (currentcharacter[0] == NULL)
			{
				player[0].number = rand()%3;
				currentcharacter[0] = character[player[0].number];
				
				colors[0] = 0;
				net_assign_tintcolor(0, colors);
				
				data.clear();
				data.appendByte(NET_CHARACTER);
				data.appendByte(0);
				data.appendByte(player[0].number);
				net.engine->broadcastTCP(data);
				
				data.clear();
				data.appendByte(NET_PLAYERTINT);
				data.appendByte(0);
				data.appendVariable(player[0].tintcolor);
				net.engine->broadcastTCP(data);
			}
			
			for (unsigned int i = 0; i < clients.size(); i++)
			{
				int pnum = i + 1;

				if (clients[i].isai)
					player[pnum].controller = AI;
				else
					player[pnum].controller = NET;
				
				if (currentcharacter[pnum] == NULL)
				{
					player[pnum].number = rand()%character.size();
					currentcharacter[pnum] = character[player[pnum].number];
					colors[pnum]++;
					
					net_assign_tintcolor(pnum, colors);
					
					data.clear();
					data.appendByte(NET_CHARACTER);
					data.appendByte(pnum);
					data.appendByte(player[pnum].number);
					net.engine->broadcastTCP(data);
					
					data.clear();
					data.appendByte(NET_PLAYERTINT);
					data.appendByte(pnum);
					data.appendVariable(player[pnum].tintcolor);
					net.engine->broadcastTCP(data);
				}
			}
			
			//load_ai is called when the map is first selected, no need to repeat it here
			
			create_sidebars();
			get_connected_beaches();
			get_teleports();
			
			data.clear();
			data.appendByte(NET_GO);
			net.engine->broadcastTCP(data);
			
			game->game_just_started = true;
			game->loaded_from_file = false;
			first = true;
			return NET_PLAY;
		}
	}
	
	if (back.clicked())
	{  //disconnect from the lobby, and give the clients some time to disconnect before leaving
		DyBytes leavedata;
		net.engine->disableTCPListen();
		net.chatengine->disableTCPListen();
		net.lobbyengine->disconnectHost(net.lobbythread);
		
		leavedata.appendByte(NET_DISCONNECT);
		net.engine->broadcastTCP(leavedata);
		
		returning_to_lobby = true;
	}
	
	if (!net.lobbyengine->isConnected(net.lobbythread))
	{
		buffer_textout_right_ex(normal, "Error: Lost connection to server, your game won't be listed!", 750, 10, BLACK);
	}
	else
	{
		DyBytes lobbydata;
		if (net.lobbyengine->getMessage(net.lobbythread, lobbydata))
		{
			string id = getID(lobbydata);
			
			if (id == PORT_ERROR)
			{
				chatbox.additem("Warning: You don't seem to be available through ports 41136/41137.  This probably means nobody will be able to connect to your game!");
				chatbox.additem("If you're behind a router, make sure those ports are forwarded");
				chatbox.scroll_to_end();
			}
		}
		
		if (last_lobby_update <= 0)
		{
			last_lobby_update = 250;
			send_lobbydata(mapbox.selected());
		}
		else last_lobby_update--;
	}
	
	draw_mouse();
	
	return NET_HOST;
}

bool hostcommand(_listbox *chatbox, string str)
{
	if (str.substr(0, str.find(" ")) == "/list")
	{
		for (int i = 0; i < 3; i++)
		{
			if (net.clientthread[i] != -1)
			{
				char c[100];
				
				sprintf(c, "Network slot %d: %s", i, player[i + 1].name.c_str());
				chatbox->additem(c);
				chatbox->scroll_to_end();
			}
		}
		return true;
	}
	
	if (str.substr(0, str.find(" ")) == "/AIify")
	{
		stringstream s;
		string dummy;
		int p;
		
		s << str;
		s >> dummy >> p;
		
		if ((player[p].controller == AI) || (player[p].controller == NETAI))
			return true;  //already AI, don't do anything
		
		if ((p > -1) && (p < numplayers))
		{
			
			if (p == 0)
				player[p].controller = AI;
			else
				player[p].controller = NETAI;
			
			for (int i = 0; i < 3; i++)
			{
				if (net.clientthread[i] != -1)
				{
					DyBytes data;
					
					data.appendByte(NET_NEWCONTROLLER);
					data.appendByte(p);
					
					if (i + 1 == p)
						data.appendByte(AI);
					else
						data.appendByte(NETAI);
					
					net.engine->sendTCP(net.clientthread[i], data);
				}
			}
			
			
			
		}
		else
		{
			chatbox->additem("Can't AIify slot, number given was bad.  The format is \"/AIify <player (0-3)>\"");
			chatbox->scroll_to_end();
		}
		
		return true;
	}
	return false;
}

bool do_hostchat(_listbox *chatbox, string str)
{  //returns true if a new item has been added
	bool result = false;
	unsigned int i;
	string chatstr;
	DyBytes data;
	
	if ((str != "/NOTHING YET/") && (str != ""))
	{
		if (hostcommand(chatbox, str))
		{  //if this string was a command by the host to do something, it isn't a chat message
			return false;
		}
		
		chatstr = global.netname;
		chatstr += ": ";
		chatstr += str;
		chatbox->additem(chatstr.c_str());
		chatbox->scroll_to_end();
		
		broadcast_chat_message(chatstr);
	}
	
	i = 0;
	while (i < net.chatengine->getMaxConnections())
	{
		if (net.chatengine->isConnected(i))
		{
			if (net.chatengine->getMessage(i, data))
			{
				chatstr = data.getAsString();
				
				broadcast_chat_message(chatstr);
				chatbox->additem(chatstr.c_str());
				chatbox->scroll_to_end();
				result = true;
			}
		}
		i++;
	}
	return result;
}

void broadcast_chat_message(string mesg)
{
	unsigned int i = 0;
	DyBytes data;
	data.appendString(mesg);
	
	while (i < net.chatengine->getMaxConnections())
	{
		if (net.chatengine->isConnected(i))
		{
			net.chatengine->sendTCP(i, data);
		}
		i++;
	}
}

void broadcast_unreadyall()
{
	DyBytes data;
	
	for (vector<hostclient>::iterator it = clients.begin(); it != clients.end(); it++)
		it->ready = false;
	
	data.appendByte(NET_UNREADYALL);
	net.engine->broadcastTCP(data);
}

void send_inidata(int thread)
{
	int i = 0, j, k;
	DyBytes data, alldata;
	alldata.clear();
	data.clear();
	
	while (i < 3)
	{
		data.appendVariable(armystats[i].pricefraction);
		data.appendVariable(armystats[i].movemod);
		data.appendVariable(armystats[i].rangemaxmod);
		j = 0;
		while (j < 3)
		{
			data.appendVariable(armystats[i].attackfraction[j]);
			data.appendVariable(armystats[i].resistfraction[j]);
			j++;
		}
		i++;
	}
	
	data.appendByte(NUM_UNITS);
	i = 0;
	while (i < NUM_UNITS)
	{
		j = unitstats[i].name.length();
		data.appendVariable(j);
		k = 0;
		while (k < j)
		{
			data.appendByte(unitstats[i].name[k]);
			k++;
		}
		j = unitstats[i].description.length();
		data.appendVariable(j);
		k = 0;
		while (k < j)
		{
			data.appendByte(unitstats[i].description[k]);
			k++;
		}
		
		data.appendVariable(unitstats[i]._move);
		data.appendVariable(unitstats[i].movetype);
		data.appendVariable(unitstats[i].attacktype);
		data.appendVariable(unitstats[i].basetype);
		data.appendVariable(unitstats[i]._rangemin);
		data.appendVariable(unitstats[i]._rangemax);
		data.appendVariable(unitstats[i]._price);
		
		data.appendVariable(unitstats[i].loadcapacity);
		data.appendVariable(unitstats[i].loadtype);
		data.appendVariable(unitstats[i].canhit);
		data.appendVariable(unitstats[i].attackparticle);
		data.appendVariable(unitstats[i].attacksound);
		data.appendVariable(unitstats[i].movesound);
		data.appendVariable(unitstats[i].moveparticle);
		data.appendVariable(unitstats[i].cancapture);
		data.appendVariable(unitstats[i].isarmy[0]);
		data.appendVariable(unitstats[i].isarmy[1]);
		data.appendVariable(unitstats[i].isarmy[2]);
		data.appendVariable(unitstats[i].techlevel);
		data.appendVariable(unitstats[i].attacksburn);
		//don't transfer unitstats[i].healthdrawhack, that's something that depends on the client's pngs
		data.appendVariable(unitstats[i].blastradius);
		data.appendVariable(unitstats[i].attackdelay);
		
		j = 0;
		while (j < 3)
		{
			data.appendVariable(unitstats[i]._attack[j]);
			data.appendVariable(unitstats[i]._resist[j]);
			j++;
		}
		i++;
	}
	data.compress(9);
	
	alldata.appendByte(NET_INIDATA);
	alldata.append(&data);
	net.engine->sendTCP(thread, alldata);
}

void send_lobbydata(string mapname)
{
	listedgame l;
	DyBytes data;
	
	l.update(clients.size() + 1, numplayers, "", mapname, worldmap.l, worldmap.h, minimap.data_set, minimap.data_num);
	l.append_to(data);
	data.compress(9);
	prependID(data, GAME_UPDATE);
	
	net.lobbyengine->sendTCP(net.lobbythread, data);
}

void send_playerdata(int thread, int placement)
{
	unsigned int j;
	string s;
	DyBytes data;
	
	data.clear();
	data.appendByte(placement);
	
	for (unsigned int i = 0; i < 4; i++)
	{
		if ((i == 0) || ((i < clients.size() + 1)))
		{
			if ((i != 0) && !clients[i - 1].isai)
			{  //if it's not the host player or an AI player
				if (player[i].name == "")
				{  //haven't received this player's name yet
					data.appendByte(0);  //add a 0 to data and go to the next player
					continue;
				}
			}
			
			j = player[i].name.length();
			data.appendByte(j);
			data.appendString(player[i].name);
			data.appendByte(player[i].number);

			if (player[i].controller == HUMAN) data.appendByte(NET);
			else if (player[i].controller == AI) data.appendByte(NETAI);
			else data.appendByte(player[i].controller);
			
			data.appendByte(player[i].team);
			data.appendVariable(player[i].tintcolor);
			
			s = net.address[i];
			j = s.length();
			data.appendByte(j);
			data.appendString(s);
		}
		else
		{  //no player for this slot, add a 0 and continue
			data.appendByte(0);
		}
	}
	data.compress(9);
	data.insertByte(NET_PLAYERDATA);
	
	net.engine->sendTCP(thread, data);
}

DyBytes menudata()
{
	DyBytes data;
	data.clear();
	
	data.appendByte(game->cashturns);
	data.appendByte(player[0].cash / 1000);
	data.appendByte(game->techlevel);
	data.appendByte(gameoptions.funding / 1000);
	data.appendByte(gameoptions.show_damage);
	data.compress(9);
	data.insertByte(NET_MENUDATA);
	return data;
}

void send_menudata(int thread)
{
	DyBytes data = menudata();
	net.engine->sendTCP(thread, data);
}

void broadcast_menudata()
{
	DyBytes data = menudata();
	net.engine->broadcastTCP(data);	
}

bool load_client_playerdata(int p, DyBytes *data)
{
	unsigned int namelen, i;
	char str[50];
	
	namelen = data->getByte(0);
	data->removeFront(1);
	if (namelen > data->size()) return false;
	if (namelen >= 50) return false;
	
	i = 0;
	while (i < namelen)
	{
		str[i] = data->getByte(0);
		data->removeFront(1);
		i++;
	}
	str[i] = '\0';
	player[p].name = str;
	
	return true;
}

void do_netai_options(_button *b, int *colors, _button *ccyc, _button *pcyc, _button *tcyc)
{
	for (unsigned int i = 0; ((i < clients.size() + 1) && (i < 3)); i++)
	{
		if (i == clients.size() || clients[i].isai)
		{  //if this client doesn't exist yet, or exists and is an AI player
			
			if (i < clients.size())
			{  //client exists, must be an AI player
				glb.draw(55, 200, 186 + 116 * i);  //draw the little AI image
			}
			
			b[i].check();
			if (b[i].clicked())
			{
				if (i == clients.size())
				{  //create a new AI player
					int pnum = i + 1;
					
					clients.push_back(hostclient());
					clients[i].isai = true;
					
					player[pnum].number = 0;
					player[pnum].team = i + 2;
					player[pnum].name = "AI";
					player[pnum].controller = AI;
					player[pnum].playing = 1;
					player[pnum].everplaying = 1;
					colors[pnum] = 0;
					net_assign_tintcolor(pnum, colors);
					currentcharacter[pnum] = character[player[pnum].number];
					
					pcyc[pnum].picture = currentcharacter[pnum]->picture;
					tcyc[pnum].picture = glb.bmp(13 + player[pnum].team);
					ccyc[pnum].color = player[pnum].tintcolor;
					
					for (int j = 0; j < 3; j++)
						if (clientconnected(j))
							send_playerdata(net.clientthread[j], j + 1);
				}
				else
				{  //remove an existing AI player
					remove_player(i, colors, ccyc, pcyc, tcyc);
					i--;
				}
			}
		}
	}
}

void remove_player(int cthread, int *colors, _button *ccyc, _button *pcyc, _button *tcyc)
{
	int pnum = cthread + 1;
	
	if (net.clientthread[cthread] != -1)  //if this isn't an AI player, the player's connection needs to be removed
		disconnect(cthread);
	
	player[pnum].name = "";
	player[pnum].playing = 0;
	player[pnum].everplaying = 0;
	player[pnum].team = pnum;
	player[pnum].number = 0;
	player[pnum].tintcolor = player_color(0, 0);
	currentcharacter[pnum] = NULL;
	
	for (unsigned int k = cthread; k < clients.size() - 1; k++)
	{  //if the guy who was removed was in the middle, move everyone below him up one to fill the gap
		pnum = k + 1;
		
		if (clientconnected(k + 1))
		{
			net.clientthread[k] = net.clientthread[k + 1];
			net.clientthread[k + 1] = -1;
		}
		
		player[pnum].name = player[pnum + 1].name;
		player[pnum].team = player[pnum + 1].team;
		player[pnum].number = player[pnum + 1].number;
		player[pnum].controller = player[pnum + 1].controller;
		player[pnum].tintcolor = player[pnum + 1].tintcolor;
		currentcharacter[pnum] = currentcharacter[pnum + 1];
		colors[pnum] = colors[pnum + 1];
		
		
		player[pnum + 1].name = "";
		player[pnum + 1].team = 1;
		player[pnum + 1].number = 0;
		player[pnum + 1].controller = HUMAN;
		player[pnum + 1].tintcolor = player_color(0, 0);
		colors[pnum + 1] = 0;
		currentcharacter[pnum + 1] = NULL;
		
		if (clients[k].isai)
		{
			pcyc[pnum].picture = currentcharacter[pnum]->picture;
			tcyc[pnum].picture = glb.bmp(13 + player[pnum].team);
			ccyc[pnum].color = player[pnum].tintcolor;
		}
	}
	
	for (int k = 0; k < 3; k++)
		if (clientconnected(k))
			send_playerdata(net.clientthread[k], k + 1);
	
	clients.erase(clients.begin() + cthread);
}

void net_assign_tintcolor(int p, int *colors)
{
	if (colors[p] > 3) colors[p] = 0;
	player[p].tintcolor = player_color(player[p].number, colors[p]);
	
	for (int k = 0; k < 4; k++)
	{  //go through and ensure that everyone has different colors
		if (unsigned(k) > clients.size())  //if we're out of players to compare colors with, break out of the loop
			break;
		
		if (p != k)
		{  //if this isn't the player being assigned the new color
			if (player[k].tintcolor == player[p].tintcolor)
			{  //duplicate color, set the player to the next possible color and try again
				colors[p]++;
				if (colors[p] > 3) colors[p] = 0;
				
				player[p].tintcolor = player_color(player[p].number, colors[p]);
				
				k = -1;  //have to go through all the players over again
			}
		}
	}
}

void disconnect(int thread)
{
	if (net.engine->isConnected(thread))
		net.engine->disconnectHost(thread);
	
	if (net.chatengine->isConnected(thread))
		net.chatengine->disconnectHost(thread);
}

void cycle_startcash(int amount, _button &startingcash)
{
	char c[50];
	DyBytes data;
	
	player[0].cash += amount;
	
	if (player[0].cash > 50000) player[0].cash = 0;
	if (player[0].cash < 0)     player[0].cash = 50000;
	
	for (unsigned int i = 1; i < 4; i++)
		player[i].cash = player[0].cash;

	sprintf(c, "Starting Cash: %d", player[0].cash);
	startingcash.init(348, 410, 170, 20, c);
	broadcast_unreadyall();
	
	data.appendByte(NET_STARTINGCASH);
	data.appendByte(player[0].cash / 1000);
	net.engine->broadcastTCP(data);
}

void cycle_funding(int amount, _button &fundsperbuilding)
{
	char c[50];
	DyBytes data;
	
	gameoptions.funding += amount;

	if (gameoptions.funding > 10000) gameoptions.funding = 1000;
	if (gameoptions.funding < 1000)  gameoptions.funding = 10000;

	sprintf(c, "Funds per Building: %d", gameoptions.funding);
	fundsperbuilding.init(348, 431, 170, 20, c);
	broadcast_unreadyall();
	
	data.appendByte(NET_FUNDSPERBUILDING);
	data.appendByte(gameoptions.funding / 1000);
	net.engine->broadcastTCP(data);
}

void cycle_techlevel(int amount, _button &techlevel)
{
	char c[50];
	DyBytes data;
	
	game->techlevel += amount;
	
	if (game->techlevel > 3) game->techlevel = 1;
	if (game->techlevel < 1) game->techlevel = 3;
	
	sprintf(c, "Tech Level: %d", game->techlevel);
	techlevel.init(348, 473, 170, 20, c);
	broadcast_unreadyall();
	
	data.appendByte(NET_TECHLEVEL);
	data.appendByte(game->techlevel);
	net.engine->broadcastTCP(data);
}

void cycle_cashturns(int amount, _button &cashturns)
{
	char c[50];
	DyBytes data;
	
	game->cashturns += amount;
	if (game->cashturns > 30) game->cashturns = 0;
	if (game->cashturns < 0) game->cashturns = 30;
	
	if (game->cashturns == 0)
	{
		cashturns.init(348, 494, 170, 20, "Derricks Last: Forever");
	}
	else
	{
		sprintf(c, "Derricks Last: %d Turns", game->cashturns);
		cashturns.init(348, 494, 170, 20, c);
	}
	broadcast_unreadyall();
	
	data.appendByte(NET_CASHTURNS);
	data.appendByte(game->cashturns);
	net.engine->broadcastTCP(data);
}
