#include "empire.h"

/*
Lua functions that can be called from C++:

serialize( string or number )
read_savefile_name(path)
load_savefile(path)
*/

string spaces(unsigned int num)
{
	stringstream s;
	
	for (unsigned int i = 0; i < num; i++)
		s << " ";
	
	return s.str();
}

string lua_serialized(const string &str)
{
	lua_State *L = lua_state::get_instance();
	string r;
	
	lua_getglobal(L, "serialize");
	lua_pushstring(L, str.c_str());
	
	if (lua_pcall(L, 1, 1, 0) != 0)
	{
		stringstream s;
		s << "Error serializing string " << str << ": " << lua_tostring(L, -1);
		empdebug(s.str());
		return "''";
	}
	
	if (!lua_isstring(L, -1))
	{
		empdebug("Error, returned serialization thing wasn't a string");
		return "''";
	}
	
	r = lua_tostring(L, -1);
	lua_pop(L, 1);
	return r;
}

bool save_game_to_file(const string &savetitle, const string &path)
{
	ofstream out(path.c_str());
	if (out.fail())
	{
		stringstream s;
		s << "Error writing to " << path;
		empdebug(s.str());
		return false;
	}
	
	out << "savedgame {\n";
	out << "\tname = " << lua_serialized(savetitle) << ",\n";
	
	out << "\tmap = {name = " << lua_serialized(worldmap.get_name()) << ",\n";
	out << "\t       length = " << worldmap.get_length() << ",\n";
	out << "\t       height = " << worldmap.get_height() << ",\n";
	out << "\t       tiles = {";
	for (int y = 0; y < worldmap.get_height(); y++)
	{
		if (y != 0)
			out << "\t" << spaces(16);
		
		for (int x = 0; x < worldmap.get_length(); x++)
		{
			const _tile *tile = worldmap.get_tile(x, y);
			
			out << "{num=" << ((tile->get_set() * 100) + tile->get_number());
			
			if (tile->get_owner() != -1)
				out << ",owner=" << tile->get_owner();
			
			if (tile->burning)
				out << ",burnedfor=" << tile->burnedfor;
			
			if (tile->turnsused > 0)
				out << ",usedfor=" << tile->turnsused;
			
			out << "},";
		}
		out << "\n";
	}
	out << "\t" << spaces(16) << "},  --end of tiles\n";
	out << "\t      },  --end of map\n\n";
	
	out << "\toptions = {turn =       " << game->pturn << ",\n";
	out << "\t           funding =    " << gameoptions.funding << ",\n";
	out << "\t           showdamage = " << gameoptions.show_damage << ",\n";
	out << "\t           timelimit =  " << gameoptions.time << ",\n";
	out << "\t           time =       " << game->time << ",\n";
	out << "\t           techlevel =  " << game->techlevel << ",\n";
	out << "\t           oilturns =   " << game->cashturns << "},\n\n";
	
	out << "\tplayers = {\n";
	for (int i = 0; i < 4; i++)
	{
		out << "\t" << spaces(11) << "{playing = " << player[i].playing << ",\n";
		
		if (player[i].playing == 0)
			out << "\t" << spaces(12) << "waseverplaying = " << player[i].everplaying << ",\n";
		
		if (player[i].everplaying == 1)
		{
			out << "\t" << spaces(12) << "name = " << lua_serialized(player[i].name) << ",\n";
			out << "\t" << spaces(12) << "army = " << player[i].number << ",\n";
			out << "\t" << spaces(12) << "team = " << player[i].team << ",\n";
			out << "\t" << spaces(12) << "tintcolor = " << player[i].tintcolor << ",\n";
			out << "\t" << spaces(12) << "controller = " << player[i].controller << ",\n";
			out << "\t" << spaces(12) << "cash = " << player[i].cash << ",\n";
			
			out << "\t" << spaces(12) << "stats={raised = " << player[i].stats.raised << ",\n";
			out << "\t" << spaces(12) << "       spent = " << player[i].stats.spent << ",\n";
			out << "\t" << spaces(12) << "       produced = " << player[i].stats.produced << ",\n";
			out << "\t" << spaces(12) << "       killed = " << player[i].stats.killed << ",\n";
			out << "\t" << spaces(12) << "       lost = " << player[i].stats.lost << "},\n";
			
			out << "\t" << spaces(12) << "units = {\n";
			for (int x = 0; x < 50; x++)
			{
				if (player[i].unit[x].exists == 1)
				{
					const _unit *u = &player[i].unit[x];
					
					out << "\t" << spaces(21) << "{type = " << u->type << ",\n";
					out << "\t" << spaces(22) << "x = " << u->tilex << ",\n";
					out << "\t" << spaces(22) << "y = " << u->tiley << ",\n";
					out << "\t" << spaces(22) << "health = " << u->health << ",\n";
					
					if (u->attackdelay != 0)
						out << "\t" << spaces(22) << "attackdelay = " << u->attackdelay << ",\n";
					
					out << "\t" << spaces(22) << "canmove = " << u->canmove << ",\n";
					out << "\t" << spaces(22) << "ready = " << u->ready << ",\n";
					
					if ((u->load[0].loaded == 1) || (u->load[1].loaded == 1))
					{
						out << "\t" << spaces(22) << "loadedunits = {\n";
						
						for (int l = 0; l < 2; l++)
						{
							if (u->load[l].loaded == 1)
							{
								out << "\t" << spaces(37) << "{type = " << u->load[l].type << ",\n";
								out << "\t" << spaces(38) << "health = " << u->load[l].health << ",\n";
								
								if ((u->load[l].subload[0].loaded == 1) || (u->load[l].subload[1].loaded == 1))
								{
									out << "\t" << spaces(38) << "subloads = {\n";
									
									for (int s = 0; s < 2; s++)
									{
										if (u->load[l].subload[s].loaded == 1)
										{
											out << "\t" << spaces(50) << "{type = " << u->load[l].subload[s].type << ", health = " << u->load[l].subload[s].health << "},\n";
										}
									}
									out << "\t" << spaces(49) << "},\n";  //end of loaded unit's subloads
								}
								out << "\t" << spaces(37) << "},\n";  //end of loaded unit 
							}
						}
						out << "\t" << spaces(36) << "},\n";  //end of unit loads
					}
					out << "\t" << spaces(21) << "},\n";  //end of unit
				}
			}
			out << "\t" << spaces(20) << "},  --end of units for player " << i << "\n";
		}
		out << "\t" << spaces(11) << "},  --end of player " << i << "\n";
	}
	out << "\t" << spaces(10) << "},  --end of players\n";
	
	out << "}\n";  //game
	
	return true;
}

string read_savefile_name(const string &path)
{
	string name;
	lua_State *L = lua_state::get_instance();
	
	lua_getglobal(L, "read_savefile_name");
	lua_pushstring(L, path.c_str());
	
	if (lua_pcall(L, 1, 1, 0) != 0)
	{
		stringstream s;
		s << "Error reading save file " << path << ": " << lua_tostring(L, -1);
		empdebug(s.str());
		return "";
	}
	
	if (!lua_isstring(L, -1))
	{
		stringstream s;
		s << "Error, save file " << path << " has a name that isn't a string";
		empdebug(s.str());
		return "";
	}
	
	name = lua_tostring(L, -1);
	lua_pop(L, 1);
	return name;
}








//============

/*
Functions Lua needs to call to load a game:

set_mapname(string)
set_mapdims(int, int)   (l, h)
set_maptile(int, int, table)   (x, y, table containing num, owner, burnedfor, usedfor)
set_mappplayers(int)
set_playerdata(int, table)  (player index, table containing name, army number, team number, tint number, controller, cash)
set_playerstats(int, table)  (player index, table containing cash raised, cash spent, units produced, units lost, enemy units killed)
set_gameoptions(table)  (table containing {player turn, funds per building, show damage, time limit, time, tech level, turns before oil derricks expire})
add_unit(int, table) (player index, table containing type, tx, ty, health, attackdelay, canmove, ready), RETURNS INDEX OF ADDED UNIT
add_unit_load(int, int, table)  (player index, unit index, table containing type, health), RETURNS INDEX OF ADDED LOAD
add_unit_subload(int, int, int, table)  (player index, unit index, load index, table containing type, health)
*/

static int get_int_field(lua_State *L, const char *fieldname)
{  //assumes the table is at the top of the stack
	int result;
	
	lua_pushstring(L, fieldname);  //push the fieldname onto the stack
	lua_gettable(L, -2);  //call lua_gettable with the stack index of the table
	
	//Lua will pop the fieldname from the stack and push the result
	if (!lua_isnumber(L, -1))
	{
		luaL_error(L, "Error reading fieldname %s, was expecting a number but didn't get it", fieldname);
		return 0;
	}
	
	result = (int)lua_tointeger(L, -1);
	lua_pop(L, 1);  //pop the result, to put the stack back as it was before this function was called
	return result;
}

static float get_float_field(lua_State *L, const char *fieldname)
{
	float result;
	
	lua_pushstring(L, fieldname);  //push the fieldname onto the stack
	lua_gettable(L, -2);  //call lua_gettable with the stack index of the table
	
	//Lua will pop the fieldname from the stack and push the result
	if (!lua_isnumber(L, -1))
	{
		luaL_error(L, "Error reading fieldname %s, was expecting a number but didn't get it", fieldname);
		return 0;
	}
	
	result = (float)lua_tonumber(L, -1);
	lua_pop(L, 1);  //pop the result, to put the stack back as it was before this function was called
	return result;
}

static const char *get_string_field(lua_State *L, const char *fieldname)
{
	const char *result;
	
	lua_pushstring(L, fieldname);  //push the fieldname onto the stack
	lua_gettable(L, -2);  //call lua_gettable with the stack index of the table
	
	//Lua will pop the fieldname from the stack and push the result
	result = lua_tostring(L, -1);
	if (result == NULL)  //if there was an error reading the string, result will be NULL
	{
		luaL_error(L, "Error reading fieldname %s, was expecting a string but didn't get it", fieldname);
		return "";
	}
	
	lua_pop(L, 1);  //pop the result, to put the stack back as it was before this function was called
	return result;
}

static int set_mapname(lua_State *L)
{
	const char *c = lua_tostring(L, -1);
	
	if (c == NULL)
	{
		luaL_error(L, "Couldn't set map name: Map name was not a string");
		return 0;
	}
	
	worldmap.set_name(c);
	return 0;
}

static int set_mapdims(lua_State *L)
{
	int x = lua_tointeger(L, 1);
	int y = lua_tointeger(L, 2);
	
	if ((x <= 0) || (x > MAP_MAX_L))
	{
		luaL_error(L, "Couldn't set map dimensions, length is %d", x);
		return 0;
	}
	if ((y <= 0) || (y > MAP_MAX_H))
	{
		luaL_error(L, "Couldn't set map dimensions, height is %d", y);
		return 0;
	}
	
	worldmap = _map(x, y);
	return 0;
}

static int set_maptile(lua_State *L)
{
	int x, y;
	int num, owner, burnedfor, usedfor;
	_tile *t;
	
	if ((!lua_isnumber(L, 1)) || (!lua_isnumber(L, 2)))
	{
		luaL_error(L, "Couldn't set map tile, tile location component (x or y) is not a number");
		return 0;
	}
	x = lua_tointeger(L, 1);
	y = lua_tointeger(L, 2);
	
	if ((x < 0) || (x >= worldmap.get_length()) || (y < 0) || (y >= worldmap.get_height()))
	{
		luaL_error(L, "Couldn't set map tile, tile location is out of bounds (%d,%d)", x, y);
		return 0;
	}
	
	if (!lua_istable(L, -1))
	{
		luaL_error(L, "Couldn't set map tile at (%d,%d), final set_maptile argument isn't a table", x, y);
		return 0;
	}
	
	num = get_int_field(L, "num");
	owner = get_int_field(L, "owner");
	burnedfor = get_int_field(L, "burnedfor");
	usedfor = get_int_field(L, "usedfor");
	
	t = worldmap.get_tile(x, y);
	if (t == NULL)
	{
		luaL_error(L, "Couldn't set map tile at (%d,%d), tile pointer is NULL", x, y);
		return 0;
	}
	
	t->change_type(num / 100, num % 100);
	t->change_owner(owner);
	
	if (burnedfor >= 0)
	{
		t->burning = true;
		t->burnedfor = burnedfor;
	}
	if (usedfor > 0)
	{
		t->turnsused = usedfor;
	}
	
	return 0;
}

static int set_mapplayers(lua_State *L)
{
	int players = lua_tointeger(L, 1);
	
	if ((players <= 1) || (players > 4))
	{
		luaL_error(L, "Couldn't set number of players on the map (players = %d)", players);
		return 0;
	}
	
	//check the map to make sure no tile is owned by an out-of-range player index
	for (int y = 0; y < worldmap.get_height(); y++)
	{
		for (int x = 0; x < worldmap.get_length(); x++)
		{
			int owner = worldmap.get_tile(x, y)->get_owner();
			
			if (owner < -1)
				owner = -1;
			
			if (owner > players)
			{
				luaL_error(L, "Map contains a tile belonging to a player that doesn't exist (index = %d)", owner);
				return 0;
			}
		}
	}
	
	worldmap.force_numplayers(players);
	return 0;
}

static int set_playerdata(lua_State *L)
{
	const char *name;
	int pnum;
	int playing, army, team, tintcolor, controller, cash;
	
	if (!lua_isnumber(L, 1))
	{
		luaL_error(L, "Couldn't set player data, player index is not a number");
		return 0;
	}
	
	pnum = lua_tointeger(L, 1);
	if ((pnum < 0) || (pnum >= 4))
	{
		luaL_error(L, "Couldn't set player data, player index is out of bounds (%d)", pnum);
		return 0;
	}
	
	for (int i = 0; i < pnum; i++)
	{
		if (player[i].everplaying == 0)
		{
			luaL_error(L, "Couldn't set player data at index %d, because the player at index %d is marked as having never been in this game", pnum, i);
			return 0;
		}
	}
	
	if (!lua_istable(L, -1))
	{
		luaL_error(L, "Couldn't set player data, final argument to set_playerdata is not a table");
		return 0;
	}
	
	name = get_string_field(L, "name");
	playing = get_int_field(L, "playing");
	army = get_int_field(L, "army");
	team = get_int_field(L, "team");
	tintcolor = get_int_field(L, "tintcolor");
	controller = get_int_field(L, "controller");
	cash = get_int_field(L, "cash");
	
	if ((army < 0) || (army > 2))
	{
		luaL_error(L, "Couldn't set up player %d, because the army field is out of bounds (%d)", pnum, army);
		return 0;
	}
	
	if ((team < 1) || (team > 4))
	{
		luaL_error(L, "Couldn't set up player %d, because the team field is out of bounds (%d)", pnum, team);
		return 0;
	}
	
	if ((controller < 0) || (controller >= MAX_CONTROLLERS))
	{
		luaL_error(L, "Couldn't set up player %d, because its controller was out of bounds (%d)", pnum, controller);
		return 0;
	}
	
	if (cash < 0)
		cash = 0;
	if (cash > 1000000)
		cash = 1000000;
	
	if (name == NULL)
	{
		stringstream s;
		s << "Player " << pnum + 1;
		player[pnum].name = s.str();
	}
	else
	{
		player[pnum].name = name;
	}
	
	player[pnum].everplaying = 1;
	player[pnum].playing = (playing > 0) ? 1 : 0;
	player[pnum].number = army;
	currentcharacter[pnum] = character[player[pnum].number];
	player[pnum].team = team;
	player[pnum].tintcolor = tintcolor;
	player[pnum].controller = controller;
	player[pnum].cash = cash;
	
	return 0;
}

static int set_playerstats(lua_State *L)
{
	int pnum;
	int totalcash, totalspent, totalproduced, totallost, totalkilled;
	
	if (!lua_isnumber(L, 1))
	{
		luaL_error(L, "Couldn't set player data, player index is not a number");
		return 0;
	}
	
	pnum = lua_tointeger(L, 1);
	if ((pnum < 0) || (pnum >= 4))
	{
		luaL_error(L, "Couldn't set player data, player index is out of bounds (%d)", pnum);
		return 0;
	}
	
	if (player[pnum].everplaying == 0)
	{
		luaL_error(L, "Couldn't set player stats at index %d, because the player is marked as having never been in this game", pnum);
		return 0;
	}
	
	if (!lua_istable(L, -1))
	{
		luaL_error(L, "Couldn't set player stats at index %d, final argument to set_playerstats is not a table", pnum);
		return 0;
	}
	
	totalcash = get_int_field(L, "raised");
	totalspent = get_int_field(L, "spent");
	totalproduced = get_int_field(L, "produced");
	totallost = get_int_field(L, "lost");
	totalkilled = get_int_field(L, "killed");
	
	//these are just useless stats, we don't really care if they're incorrect, so no validation code
	
	player[pnum].stats.raised = totalcash;
	player[pnum].stats.spent = totalspent;
	player[pnum].stats.produced = totalproduced;
	player[pnum].stats.lost = totallost;
	player[pnum].stats.killed = totalkilled;
	return 0;
}

static int set_gameoptions(lua_State *L)
{
	if (!lua_istable(L, -1))
	{
		luaL_error(L, "Couldn't set game options, argument to set_gameoptions is not a table");
		return 0;
	}
	
	game->pturn = get_int_field(L, "turn");
	gameoptions.funding = get_int_field(L, "funding");
	gameoptions.show_damage = get_int_field(L, "showdamage");
	gameoptions.time = get_int_field(L, "timelimit");
	game->time = get_int_field(L, "time");
	game->techlevel = get_int_field(L, "techlevel");
	game->cashturns = get_int_field(L, "oilturns");
	
	if (gameoptions.time <= 0)
		gameoptions.time = -999;
	else if ((game->time > gameoptions.time) || (game->time <= 0))
		game->time = gameoptions.time;
	
	if (gameoptions.funding < 0)
		gameoptions.funding = 0;
	
	if ((game->pturn < 0) || (game->pturn >= 4))
	{
		luaL_error(L, "Error setting game options, player turn (%d) was not in bounds", game->pturn);
		return 0;
	}
	
	if (game->techlevel < 1)
		game->techlevel = 1;
	if (game->techlevel > 3)
		game->techlevel = 3;
	
	if (game->cashturns < 0)
		game->cashturns = 0;
	
	return 0;
}

static int add_unit(lua_State *L)
{
	int pnum;
	int type, x, y, attackdelay, canmove, ready;
	int uindex;
	float health;
	
	if (!lua_isnumber(L, 1))
	{
		luaL_error(L, "Couldn't add unit, player index is not a number");
		return 0;
	}
	
	pnum = lua_tointeger(L, 1);
	if ((pnum < 0) || (pnum >= 4))
	{
		luaL_error(L, "Couldn't add unit, player index is out of bounds (%d)", pnum);
		return 0;
	}
	
	if ((player[pnum].everplaying == 0) || (player[pnum].playing == 0))
	{
		luaL_error(L, "Couldn't add unit to player at index %d, because the player is not in the game", pnum);
		return 0;
	}
	
	if (!lua_istable(L, -1))
	{
		luaL_error(L, "Couldn't add unit to player at index %d, final argument to add_unit is not a table", pnum);
		return 0;
	}
	
	type = get_int_field(L, "type");
	x = get_int_field(L, "x");
	y = get_int_field(L, "y");
	attackdelay = get_int_field(L, "attackdelay");
	canmove = get_int_field(L, "canmove");
	ready = get_int_field(L, "ready");
	health = get_float_field(L, "health");
	
	if ((type < 0) || (type >= NUM_UNITS))
	{
		luaL_error(L, "Couldn't add unit to player at index %d, bad unit type number: %d", pnum, type);
		return 0;
	}
	if ((x < 0) || (x >= worldmap.get_length()) || (y < 0) || (y >= worldmap.get_height()) || (any_unit_here(x, y) != -1))
	{
		luaL_error(L, "Couldn't add unit to player at index %d, the tile location was either out of bounds or already occupied (%d, %d)", pnum, x, y);
		return 0;
	}
	if (attackdelay < 0)
		attackdelay = 0;
	canmove = (canmove > 0) ? 1 : 0;
	ready = (ready > 0) ? 1 : 0;
	if ((health <= 0) || (health > 10))
	{
		luaL_error(L, "Couldn't add unit to player at index %d, invalid value for health (%f)", pnum, health);
		return 0;
	}
	
	uindex = player[pnum].create_unit(type, x, y, pnum);
	if (uindex < 0)
	{
		luaL_error(L, "Couldn't add unit to player at index %d, creating the unit failed", pnum);
		return 0;
	}
	player[pnum].unit[uindex].attackdelay = attackdelay;
	player[pnum].unit[uindex].canmove = canmove;
	player[pnum].unit[uindex].ready = ready;
	player[pnum].unit[uindex].health = health;
	
	lua_pushnumber(L, uindex);
	return 1;
}

static int add_unit_load(lua_State *L)
{
	int pnum, unum, freespot;
	int type;
	float health;
	
	if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2))
	{
		luaL_error(L, "Couldn't add unit load, player index or unit index is not a number");
		return 0;
	}
	
	pnum = lua_tointeger(L, 1);
	if ((pnum < 0) || (pnum >= 4))
	{
		luaL_error(L, "Couldn't add unit load, player index is out of bounds (%d)", pnum);
		return 0;
	}
	unum = lua_tointeger(L, 2);
	if ((unum < 0) || (unum >= 50))
	{
		luaL_error(L, "Couldn't add unit load, unit index is out of bounds (%d)", pnum);
		return 0;
	}
	
	if ((player[pnum].everplaying == 0) || (player[pnum].playing == 0))
	{
		luaL_error(L, "Couldn't add unit load to player %d unit %d, the player is not in the game", pnum, unum);
		return 0;
	}
	
	if (!lua_istable(L, -1))
	{
		luaL_error(L, "Couldn't add unit load to player %d unit %d, final argument to add_unit is not a table", pnum, unum);
		return 0;
	}
	
	type = get_int_field(L, "type");
	health = get_float_field(L, "health");
	
	if ((type < 0) || (type >= NUM_UNITS))
	{
		luaL_error(L, "Couldn't add unit load to player %d unit %d, bad unit type number: %d", pnum, unum, type);
		return 0;
	}
	if ((health <= 0) || (health > 10))
	{
		luaL_error(L, "Couldn't add unit load to player %d unit %d, invalid value for health (%f)", pnum, unum, health);
		return 0;
	}
	
	_unit &u = player[pnum].unit[unum];
	if (u.exists != 1)
	{
		luaL_error(L, "Couldn't add unit load, player %d unit %d doesn't exist", pnum, unum);
		return 0;
	}
	
	freespot = -1;
	for (int i = 0; i < unitstats[u.type].loadcapacity; i++)
	{
		if (u.load[i].loaded == 0)
		{
			freespot = i;
			break;
		}
	}
	if (freespot == -1)
	{
		luaL_error(L, "Couldn't add unit load, player %d unit %d doesn't have any free load slots", pnum, unum);
		return 0;
	}
	
	bool can_load = false;
	if (unitstats[u.type].loadtype == 1)
	{  //can load infantry, mech
		if ((unitstats[type].movetype == M_INFANTRY) || (unitstats[type].movetype == M_MECH))
		{
			can_load = true;
		}
	}
	else if (unitstats[u.type].loadtype == 2)
	{  //can load any land unit
		if (unitstats[type].basetype == LAND)
		{
			can_load = true;
		}
	}
	
	if (!can_load)
	{
		luaL_error(L, "Couldn't add unit load, player %d unit %d can't load type %s", pnum, unum, unit_name(type).c_str());
		return 0;
	}
	
	u.load[freespot].loaded = 1;
	u.load[freespot].type = type;
	u.load[freespot].health = health;
	
	lua_pushnumber(L, freespot);
	return 1;
}

static int add_unit_subload(lua_State *L)
{
	int pnum, unum, lnum, freespot;
	int type;
	float health;
	
	if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3))
	{
		luaL_error(L, "Couldn't add unit subload: player index, unit index, or load index is not a number");
		return 0;
	}
	
	pnum = lua_tointeger(L, 1);
	if ((pnum < 0) || (pnum >= 4))
	{
		luaL_error(L, "Couldn't add unit subload, player index is out of bounds (%d)", pnum);
		return 0;
	}
	unum = lua_tointeger(L, 2);
	if ((unum < 0) || (unum >= 50))
	{
		luaL_error(L, "Couldn't add unit subload, unit index is out of bounds (%d)", pnum);
		return 0;
	}
	lnum = lua_tointeger(L, 3);
	if ((lnum < 0) || (lnum >= 2))
	{
		luaL_error(L, "Couldn't add unit subload, load index is out of bounds (%d)", lnum);
		return 0;
	}
	
	if ((player[pnum].everplaying == 0) || (player[pnum].playing == 0))
	{
		luaL_error(L, "Couldn't add unit subload to player %d unit %d, the player is not in the game", pnum, unum);
		return 0;
	}
	
	if (player[pnum].unit[unum].exists != 1)
	{
		luaL_error(L, "Couldn't add unit subload to player %d unit %d, the unit does not exist", pnum, unum);
		return 0;
	}
	
	if (player[pnum].unit[unum].load[lnum].loaded != 1)
	{
		luaL_error(L, "Couldn't add unit subload to player %d unit %d, load slot %d is empty", pnum, unum, lnum);
		return 0;
	}
	
	if (!lua_istable(L, -1))
	{
		luaL_error(L, "Couldn't add unit load to player %d unit %d, final argument to add_unit is not a table", pnum, unum);
		return 0;
	}
	
	
	type = get_int_field(L, "type");
	health = get_float_field(L, "health");
	if ((type < 0) || (type >= NUM_UNITS))
	{
		luaL_error(L, "Couldn't add unit subload to player %d unit %d load %d, bad unit type number: %d", pnum, unum, lnum, type);
		return 0;
	}
	if ((health <= 0) || (health > 10))
	{
		luaL_error(L, "Couldn't add unit subload to player %d unit %d load %d, invalid value for health (%f)", pnum, unum, lnum, health);
		return 0;
	}
	
	
	{
		const int loadertype = player[pnum].unit[unum].load[lnum].type;
		bool can_load = false;
		
		freespot = -1;
		for (int i = 0; i < unitstats[loadertype].loadcapacity; i++)
		{
			if (player[pnum].unit[unum].load[lnum].subload[i].loaded == 0)
			{
				freespot = i;
				break;
			}
		}
		if (freespot == -1)
		{
			luaL_error(L, "Couldn't add unit subload, player %d unit %d load %d doesn't have any free load slots", pnum, unum, lnum);
			return 0;
		}
		
		if (unitstats[loadertype].loadtype == 1)
		{  //can load infantry, mech
			if ((unitstats[type].movetype == M_INFANTRY) || (unitstats[type].movetype == M_MECH))
			{
				can_load = true;
			}
		}
		else if (unitstats[loadertype].loadtype == 2)
		{  //can load any land unit
			if (unitstats[type].basetype == LAND)
			{
				can_load = true;
			}
		}
		
		if (!can_load)
		{
			luaL_error(L, "Couldn't add unit subload, player %d unit %d load %d can't load type %s", pnum, unum, lnum, unit_name(type).c_str());
			return 0;
		}
	}
	
	player[pnum].unit[unum].load[lnum].subload[freespot].loaded = 1;
	player[pnum].unit[unum].load[lnum].subload[freespot].type = type;
	player[pnum].unit[unum].load[lnum].subload[freespot].health = health;
	
	lua_pushnumber(L, freespot);
	return 1;
}







bool load_game_from_file(const string &path)
{
	static bool first = true;
	lua_State *L = lua_state::get_instance();
	
	if (first)
	{  //first time this is called, register C functions with Lua
		lua_pushcfunction(L, set_mapname);
		lua_setglobal(L, "set_mapname");
		
		lua_pushcfunction(L, set_mapdims);
		lua_setglobal(L, "set_mapdims");
		
		lua_pushcfunction(L, set_maptile);
		lua_setglobal(L, "set_maptile");
		
		lua_pushcfunction(L, set_mapplayers);
		lua_setglobal(L, "set_mapplayers");
		
		lua_pushcfunction(L, set_playerdata);
		lua_setglobal(L, "set_playerdata");
		
		lua_pushcfunction(L, set_playerstats);
		lua_setglobal(L, "set_playerstats");
		
		lua_pushcfunction(L, set_gameoptions);
		lua_setglobal(L, "set_gameoptions");
		
		lua_pushcfunction(L, add_unit);
		lua_setglobal(L, "add_unit");
		
		lua_pushcfunction(L, add_unit_load);
		lua_setglobal(L, "add_unit_load");
		
		lua_pushcfunction(L, add_unit_subload);
		lua_setglobal(L, "add_unit_subload");
		
		first = false;
	}
	
	for (int i = 0; i < 4; i++)
		player[i].reset();
	worldmap = _map(10, 10);
	
	lua_getglobal(L, "load_savefile");
	lua_pushstring(L, path.c_str());
	
	if (lua_pcall(L, 1, 0, 0) != 0)
	{
		stringstream s;
		s << "Error loading save file " << path << ": " << lua_tostring(L, -1);
		empdebug(s.str());
		return false;
	}
	
	game->game_just_started = true;
	game->loaded_from_file = true;
	game->loaded_time = game->time;
	
	return true;
}
