/*
 * Copyright 2010-2016 OpenXcom Developers.
 *
 * This file is part of OpenXcom.
 *
 * OpenXcom is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenXcom is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OpenXcom.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <assert.h>
#include <vector>
#include "BattleItem.h"
#include "SavedBattleGame.h"
#include "SavedGame.h"
#include "Tile.h"
#include "Node.h"
#include "../Mod/MapDataSet.h"
#include "../Mod/MCDPatch.h"
#include "../Battlescape/Pathfinding.h"
#include "../Battlescape/TileEngine.h"
#include "../Battlescape/BattlescapeState.h"
#include "../Battlescape/BattlescapeGame.h"
#include "../Battlescape/Position.h"
#include "../Mod/Mod.h"
#include "../Mod/Armor.h"
#include "../Engine/Game.h"
#include "../Mod/RuleInventory.h"
#include "../Battlescape/AIModule.h"
#include "../Engine/RNG.h"
#include "../Engine/Options.h"
#include "../Engine/Logger.h"
#include "SerializationHelper.h"
#include "../Mod/RuleItem.h"
 
namespace OpenXcom
{
 
/**
 * Initializes a brand new battlescape saved game.
 */
SavedBattleGame::SavedBattleGame() : _battleState(0), _mapsize_x(0), _mapsize_y(0), _mapsize_z(0), _selectedUnit(0), _lastSelectedUnit(0), _pathfinding(0), _tileEngine(0), _globalShade(0),
	_side(FACTION_PLAYER), _turn(1), _debugMode(false), _aborted(false), _itemId(0), _objectiveType(-1), _objectivesDestroyed(0), _objectivesNeeded(0), _unitsFalling(false), _cheating(false),
	_tuReserved(BA_NONE), _kneelReserved(false), _depth(0), _ambience(-1), _ambientVolume(0.5), _turnLimit(0), _cheatTurn(20), _chronoTrigger(FORCE_LOSE), _beforeGame(true)
{
	_tileSearch.resize(11*11);
	for (int i = 0; i < 121; ++i)
	{
		_tileSearch[i].x = ((i%11) - 5);
		_tileSearch[i].y = ((i/11) - 5);
	}
}
 
/**
 * Deletes the game content from memory.
 */
SavedBattleGame::~SavedBattleGame()
{
	if (_mapsize_z * _mapsize_y * _mapsize_x > 0)
	{
		for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
		{
			delete _tiles[i];
		}
		delete[] _tiles;
	}
 
	for (std::vector<MapDataSet*>::iterator i = _mapDataSets.begin(); i != _mapDataSets.end(); ++i)
	{
		(*i)->unloadData();
	}
 
	for (std::vector<Node*>::iterator i = _nodes.begin(); i != _nodes.end(); ++i)
	{
		delete *i;
	}
 
	for (std::vector<BattleUnit*>::iterator i = _units.begin(); i != _units.end(); ++i)
	{
		delete *i;
	}
 
	for (std::vector<BattleItem*>::iterator i = _items.begin(); i != _items.end(); ++i)
	{
		delete *i;
	}
	for (std::vector<BattleItem*>::iterator i = _recoverGuaranteed.begin(); i != _recoverGuaranteed.end(); ++i)
	{
		delete *i;
	}
	for (std::vector<BattleItem*>::iterator i = _recoverConditional.begin(); i != _recoverConditional.end(); ++i)
	{
		delete *i;
	}
	for (std::vector<BattleItem*>::iterator i = _deleted.begin(); i != _deleted.end(); ++i)
	{
		delete *i;
	}
 
	delete _pathfinding;
	delete _tileEngine;
}
 
/**
 * Loads the saved battle game from a YAML file.
 * @param node YAML node.
 * @param mod for the saved game.
 * @param savedGame Pointer to saved game.
 */
void SavedBattleGame::load(const YAML::Node &node, Mod *mod, SavedGame* savedGame)
{
	int mapsize_x = node["width"].as<int>(_mapsize_x);
	int mapsize_y = node["length"].as<int>(_mapsize_y);
	int mapsize_z = node["height"].as<int>(_mapsize_z);
	initMap(mapsize_x, mapsize_y, mapsize_z);
 
	_missionType = node["missionType"].as<std::string>(_missionType);
	_globalShade = node["globalshade"].as<int>(_globalShade);
	_turn = node["turn"].as<int>(_turn);
	_depth = node["depth"].as<int>(_depth);
	int selectedUnit = node["selectedUnit"].as<int>();
 
	for (YAML::const_iterator i = node["mapdatasets"].begin(); i != node["mapdatasets"].end(); ++i)
	{
		std::string name = i->as<std::string>();
		MapDataSet *mds = mod->getMapDataSet(name);
		_mapDataSets.push_back(mds);
	}
 
	if (!node["tileTotalBytesPer"])
	{
		// binary tile data not found, load old-style text tiles :(
		for (YAML::const_iterator i = node["tiles"].begin(); i != node["tiles"].end(); ++i)
		{
			Position pos = (*i)["position"].as<Position>();
			getTile(pos)->load((*i));
		}
	}
	else
	{
		// load key to how the tile data was saved
		Tile::SerializationKey serKey;
		size_t totalTiles = node["totalTiles"].as<size_t>();
 
		memset(&serKey, 0, sizeof(Tile::SerializationKey));
		serKey.index = node["tileIndexSize"].as<char>(serKey.index);
		serKey.totalBytes = node["tileTotalBytesPer"].as<Uint32>(serKey.totalBytes);
		serKey._fire = node["tileFireSize"].as<char>(serKey._fire);
		serKey._smoke = node["tileSmokeSize"].as<char>(serKey._smoke);
		serKey._mapDataID = node["tileIDSize"].as<char>(serKey._mapDataID);
		serKey._mapDataSetID = node["tileSetIDSize"].as<char>(serKey._mapDataSetID);
		serKey.boolFields = node["tileBoolFieldsSize"].as<char>(1); // boolean flags used to be stored in an unmentioned byte (Uint8) :|
 
		// load binary tile data!
		YAML::Binary binTiles = node["binTiles"].as<YAML::Binary>();
 
		Uint8 *r = (Uint8*)binTiles.data();
		Uint8 *dataEnd = r + totalTiles * serKey.totalBytes;
 
		while (r < dataEnd)
		{
			int index = unserializeInt(&r, serKey.index);
			assert (index >= 0 && index < _mapsize_x * _mapsize_z * _mapsize_y);
			_tiles[index]->loadBinary(r, serKey); // loadBinary's privileges to advance *r have been revoked
			r += serKey.totalBytes-serKey.index; // r is now incremented strictly by totalBytes in case there are obsolete fields present in the data
		}
	}
	if (_missionType == "STR_BASE_DEFENSE")
	{
		if (node["moduleMap"])
		{
			_baseModules = node["moduleMap"].as<std::vector< std::vector<std::pair<int, int> > > >();
		}
		else
		{
			// backwards compatibility: imperfect solution, modules that were completely destroyed
			// prior to saving and updating builds will be counted as indestructible.
			calculateModuleMap();
		}
	}
	for (YAML::const_iterator i = node["nodes"].begin(); i != node["nodes"].end(); ++i)
	{
		Node *n = new Node();
		n->load(*i);
		_nodes.push_back(n);
	}
 
	for (YAML::const_iterator i = node["units"].begin(); i != node["units"].end(); ++i)
	{
		UnitFaction faction = (UnitFaction)(*i)["faction"].as<int>();
		UnitFaction originalFaction = (UnitFaction)(*i)["originalFaction"].as<int>(faction);
		int id = (*i)["id"].as<int>();
		BattleUnit *unit;
		if (id < BattleUnit::MAX_SOLDIER_ID) // Unit is linked to a geoscape soldier
		{
			// look up the matching soldier
			unit = new BattleUnit(savedGame->getSoldier(id), _depth);
		}
		else
		{
			std::string type = (*i)["genUnitType"].as<std::string>();
			std::string armor = (*i)["genUnitArmor"].as<std::string>();
			// create a new Unit.
			if(!mod->getUnit(type) || !mod->getArmor(armor)) continue;
			unit = new BattleUnit(mod->getUnit(type), originalFaction, id, mod->getArmor(armor), mod->getStatAdjustment(savedGame->getDifficulty()), _depth);
		}
		unit->load(*i);
		unit->setSpecialWeapon(this, mod);
		_units.push_back(unit);
		if (faction == FACTION_PLAYER)
		{
			if ((unit->getId() == selectedUnit) || (_selectedUnit == 0 && !unit->isOut()))
				_selectedUnit = unit;
		}
		if (unit->getStatus() != STATUS_DEAD && unit->getStatus() != STATUS_IGNORE_ME)
		{
			if (const YAML::Node &ai = (*i)["AI"])
			{
				AIModule *aiModule;
				if (faction != FACTION_PLAYER)
				{
					aiModule = new AIModule(this, unit, 0);
				}
				else
				{
					continue;
				}
				aiModule->load(ai);
				unit->setAIModule(aiModule);
			}
		}
	}
	// matches up tiles and units
	resetUnitTiles();
 
	std::string fromContainer[3] = { "items", "recoverConditional", "recoverGuaranteed" };
	std::vector<BattleItem*> *toContainer[3] = {&_items, &_recoverConditional, &_recoverGuaranteed};
	for (int pass = 0; pass != 3; ++pass)
	{
		for (YAML::const_iterator i = node[fromContainer[pass]].begin(); i != node[fromContainer[pass]].end(); ++i)
		{
			if ((*i)["owner"] && !(*i)["inventoryslot"])
			{
				// skip special items from OXCE saves
				continue;
			}
			std::string type = (*i)["type"].as<std::string>();
			if (mod->getItem(type))
			{
				int id = (*i)["id"].as<int>();
				_itemId = std::max(_itemId, id);
				BattleItem *item = new BattleItem(mod->getItem(type), &id);
				item->load(*i, mod);
 
				int owner = (*i)["owner"].as<int>(-1);
				int prevOwner = (*i)["previousOwner"].as<int>(-1);
				int unit = (*i)["unit"].as<int>(-1);
 
				// match up items and units
				for (std::vector<BattleUnit*>::iterator bu = _units.begin(); bu != _units.end() && owner != -1; ++bu)
				{
					if ((*bu)->getId() == owner)
					{
						item->moveToOwner(*bu);
						break;
					}
				}
				for (std::vector<BattleUnit*>::iterator bu = _units.begin(); bu != _units.end() && prevOwner != -1; ++bu)
				{
					if ((*bu)->getId() == prevOwner)
					{
						item->setPreviousOwner(*bu);
						break;
					}
				}
				for (std::vector<BattleUnit*>::iterator bu = _units.begin(); bu != _units.end() && unit != -1; ++bu)
				{
					if ((*bu)->getId() == unit)
					{
						item->setUnit(*bu);
						break;
					}
				}
 
				// match up items and tiles
				if (item->getSlot() && item->getSlot()->getType() == INV_GROUND)
				{
					Position pos = (*i)["position"].as<Position>(Position(-1, -1, -1));
					if (pos.x != -1)
						getTile(pos)->addItem(item, mod->getInventory("STR_GROUND", true));
				}
				toContainer[pass]->push_back(item);
			}
			else
			{
				Log(LOG_ERROR) << "Failed to load item " << type;
			}
		}
	}
	_itemId++;
 
	// tie ammo items to their weapons, running through the items again
	std::vector<BattleItem*>::iterator weaponi = _items.begin();
	for (YAML::const_iterator i = node["items"].begin(); i != node["items"].end(); ++i)
	{
		if ((*i)["owner"] && !(*i)["inventoryslot"])
		{
			// skip special items from OXCE saves
			continue;
		}
		if (mod->getItem((*i)["type"].as<std::string>()))
		{
			int ammo = (*i)["ammoItem"].as<int>(-1);
			if (ammo != -1)
			{
				for (std::vector<BattleItem*>::iterator ammoi = _items.begin(); ammoi != _items.end(); ++ammoi)
				{
					if ((*ammoi)->getId() == ammo)
					{
						(*weaponi)->setAmmoItem((*ammoi));
						break;
					}
				}
			}
			 ++weaponi;
		}
	}
	_objectiveType = node["objectiveType"].as<int>(_objectiveType);
	_objectivesDestroyed = node["objectivesDestroyed"].as<int>(_objectivesDestroyed);
	_objectivesNeeded = node["objectivesNeeded"].as<int>(_objectivesNeeded);
	_tuReserved = (BattleActionType)node["tuReserved"].as<int>(_tuReserved);
	_kneelReserved = node["kneelReserved"].as<bool>(_kneelReserved);
	_ambience = node["ambience"].as<int>(_ambience);
	_ambientVolume = node["ambientVolume"].as<double>(_ambientVolume);
	_music = node["music"].as<std::string>(_music);
	_turnLimit = node["turnLimit"].as<int>(_turnLimit);
	_chronoTrigger = ChronoTrigger(node["chronoTrigger"].as<int>(_chronoTrigger));
	_cheatTurn = node["cheatTurn"].as<int>(_cheatTurn);
}
 
/**
 * Loads the resources required by the map in the battle save.
 * @param mod Pointer to the mod.
 */
void SavedBattleGame::loadMapResources(Mod *mod)
{
	for (std::vector<MapDataSet*>::const_iterator i = _mapDataSets.begin(); i != _mapDataSets.end(); ++i)
	{
		(*i)->loadData(mod->getMCDPatch((*i)->getName()));
	}
 
	int mdsID, mdID;
 
	for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
	{
		for (int part = O_FLOOR; part <= O_OBJECT; part++)
		{
			TilePart tp = (TilePart) part;
			_tiles[i]->getMapData(&mdID, &mdsID, tp);
			if (mdID != -1 && mdsID != -1)
			{
				_tiles[i]->setMapData(_mapDataSets[mdsID]->getObject(mdID), mdID, mdsID, tp);
			}
		}
	}
 
	initUtilities(mod);
	getTileEngine()->calculateSunShading();
	getTileEngine()->calculateTerrainLighting();
	getTileEngine()->calculateUnitLighting();
	getTileEngine()->recalculateFOV();
}
 
/**
 * Saves the saved battle game to a YAML file.
 * @return YAML node.
 */
YAML::Node SavedBattleGame::save() const
{
	YAML::Node node;
	if (_objectivesNeeded)
	{
		node["objectivesDestroyed"] = _objectivesDestroyed;
		node["objectivesNeeded"] = _objectivesNeeded;
		node["objectiveType"] = _objectiveType;
	}
	node["width"] = _mapsize_x;
	node["length"] = _mapsize_y;
	node["height"] = _mapsize_z;
	node["missionType"] = _missionType;
	node["globalshade"] = _globalShade;
	node["turn"] = _turn;
	node["selectedUnit"] = (_selectedUnit?_selectedUnit->getId():-1);
	for (std::vector<MapDataSet*>::const_iterator i = _mapDataSets.begin(); i != _mapDataSets.end(); ++i)
	{
		node["mapdatasets"].push_back((*i)->getName());
	}
#if 0
	for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
	{
		if (!_tiles[i]->isVoid())
		{
			node["tiles"].push_back(_tiles[i]->save());
		}
	}
#else
	// first, write out the field sizes we're going to use to write the tile data
	node["tileIndexSize"] = static_cast<char>(Tile::serializationKey.index);
	node["tileTotalBytesPer"] = Tile::serializationKey.totalBytes;
	node["tileFireSize"] = static_cast<char>(Tile::serializationKey._fire);
	node["tileSmokeSize"] = static_cast<char>(Tile::serializationKey._smoke);
	node["tileIDSize"] = static_cast<char>(Tile::serializationKey._mapDataID);
	node["tileSetIDSize"] = static_cast<char>(Tile::serializationKey._mapDataSetID);
	node["tileBoolFieldsSize"] = static_cast<char>(Tile::serializationKey.boolFields);
 
	size_t tileDataSize = Tile::serializationKey.totalBytes * _mapsize_z * _mapsize_y * _mapsize_x;
	Uint8* tileData = (Uint8*) calloc(tileDataSize, 1);
	Uint8* w = tileData;
 
	for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
	{
		if (!_tiles[i]->isVoid())
		{
			serializeInt(&w, Tile::serializationKey.index, i);
			_tiles[i]->saveBinary(&w);
		}
		else
		{
			tileDataSize -= Tile::serializationKey.totalBytes;
		}
	}
	node["totalTiles"] = tileDataSize / Tile::serializationKey.totalBytes; // not strictly necessary, just convenient
	node["binTiles"] = YAML::Binary(tileData, tileDataSize);
	free(tileData);
#endif
	for (std::vector<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i)
	{
		node["nodes"].push_back((*i)->save());
	}
	if (_missionType == "STR_BASE_DEFENSE")
	{
		node["moduleMap"] = _baseModules;
	}
	for (std::vector<BattleUnit*>::const_iterator i = _units.begin(); i != _units.end(); ++i)
	{
		node["units"].push_back((*i)->save());
	}
	for (std::vector<BattleItem*>::const_iterator i = _items.begin(); i != _items.end(); ++i)
	{
		node["items"].push_back((*i)->save());
	}
	node["tuReserved"] = (int)_tuReserved;
	node["kneelReserved"] = _kneelReserved;
	node["depth"] = _depth;
	node["ambience"] = _ambience;
	node["ambientVolume"] = _ambientVolume;
	for (std::vector<BattleItem*>::const_iterator i = _recoverGuaranteed.begin(); i != _recoverGuaranteed.end(); ++i)
	{
		node["recoverGuaranteed"].push_back((*i)->save());
	}
	for (std::vector<BattleItem*>::const_iterator i = _recoverConditional.begin(); i != _recoverConditional.end(); ++i)
	{
		node["recoverConditional"].push_back((*i)->save());
	}
	node["music"] = _music;
	node["turnLimit"] = _turnLimit;
	node["chronoTrigger"] = int(_chronoTrigger);
	node["cheatTurn"] = _cheatTurn;
 
	return node;
}
 
/**
 * Gets the array of tiles.
 * @return A pointer to the Tile array.
 */
Tile **SavedBattleGame::getTiles() const
{
	return _tiles;
}
 
/**
 * Initializes the array of tiles and creates a pathfinding object.
 * @param mapsize_x
 * @param mapsize_y
 * @param mapsize_z
 */
void SavedBattleGame::initMap(int mapsize_x, int mapsize_y, int mapsize_z, bool resetTerrain)
{
	// Clear old map data
	if (_mapsize_z * _mapsize_y * _mapsize_x > 0)
	{
		for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
		{
			delete _tiles[i];
		}
		delete[] _tiles;
	}
 
	for (std::vector<Node*>::iterator i = _nodes.begin(); i != _nodes.end(); ++i)
	{
		delete *i;
	}
	_nodes.clear();
 
	if (resetTerrain)
	{
		_mapDataSets.clear();
	}
 
	// Create tile objects
	_mapsize_x = mapsize_x;
	_mapsize_y = mapsize_y;
	_mapsize_z = mapsize_z;
	_tiles = new Tile*[_mapsize_z * _mapsize_y * _mapsize_x];
	for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
	{
		Position pos;
		getTileCoords(i, &pos.x, &pos.y, &pos.z);
		_tiles[i] = new Tile(pos);
	}
 
}
 
/**
 * Initializes the map utilities.
 * @param mod Pointer to mod.
 */
void SavedBattleGame::initUtilities(Mod *mod)
{
	delete _pathfinding;
	delete _tileEngine;
	_pathfinding = new Pathfinding(this);
	_tileEngine = new TileEngine(this, mod->getVoxelData());
}
 
/**
 * Sets the mission type.
 * @param missionType The mission type.
 */
void SavedBattleGame::setMissionType(const std::string &missionType)
{
	_missionType = missionType;
}
 
/**
 * Gets the mission type.
 * @return The mission type.
 */
std::string SavedBattleGame::getMissionType() const
{
	return _missionType;
}
 
/**
 * Sets the global shade.
 * @param shade The global shade.
 */
void SavedBattleGame::setGlobalShade(int shade)
{
	_globalShade = shade;
}
 
/**
 * Gets the global shade.
 * @return The global shade.
 */
int SavedBattleGame::getGlobalShade() const
{
	return _globalShade;
}
 
/**
 * Gets the map width.
 * @return The map width (Size X) in tiles.
 */
int SavedBattleGame::getMapSizeX() const
{
	return _mapsize_x;
}
 
/**
 * Gets the map length.
 * @return The map length (Size Y) in tiles.
 */
int SavedBattleGame::getMapSizeY() const
{
	return _mapsize_y;
}
 
/**
 * Gets the map height.
 * @return The map height (Size Z) in layers.
 */
int SavedBattleGame::getMapSizeZ() const
{
	return _mapsize_z;
}
 
/**
 * Gets the map size in tiles.
 * @return The map size.
 */
int SavedBattleGame::getMapSizeXYZ() const
{
	return _mapsize_x * _mapsize_y * _mapsize_z;
}
 
/**
 * Converts a tile index to coordinates.
 * @param index The (unique) tileindex.
 * @param x Pointer to the X coordinate.
 * @param y Pointer to the Y coordinate.
 * @param z Pointer to the Z coordinate.
 */
void SavedBattleGame::getTileCoords(int index, int *x, int *y, int *z) const
{
	*z = index / (_mapsize_y * _mapsize_x);
	*y = (index % (_mapsize_y * _mapsize_x)) / _mapsize_x;
	*x = (index % (_mapsize_y * _mapsize_x)) % _mapsize_x;
}
 
/**
 * Gets the currently selected unit
 * @return Pointer to BattleUnit.
 */
BattleUnit *SavedBattleGame::getSelectedUnit() const
{
	return _selectedUnit;
}
 
/**
 * Sets the currently selected unit.
 * @param unit Pointer to BattleUnit.
 */
void SavedBattleGame::setSelectedUnit(BattleUnit *unit)
{
	_selectedUnit = unit;
}
 
/**
 * Selects the previous player unit.
 * @param checkReselect Whether to check if we should reselect a unit.
 * @param setReselect Don't reselect a unit.
 * @param checkInventory Whether to check if the unit has an inventory.
 * @return Pointer to new selected BattleUnit, NULL if none can be selected.
 * @sa selectPlayerUnit
 */
BattleUnit *SavedBattleGame::selectPreviousPlayerUnit(bool checkReselect, bool setReselect, bool checkInventory)
{
	return selectPlayerUnit(-1, checkReselect, setReselect, checkInventory);
}
 
/**
 * Selects the next player unit.
 * @param checkReselect Whether to check if we should reselect a unit.
 * @param setReselect Don't reselect a unit.
 * @param checkInventory Whether to check if the unit has an inventory.
 * @return Pointer to new selected BattleUnit, NULL if none can be selected.
 * @sa selectPlayerUnit
 */
BattleUnit *SavedBattleGame::selectNextPlayerUnit(bool checkReselect, bool setReselect, bool checkInventory)
{
	return selectPlayerUnit(+1, checkReselect, setReselect, checkInventory);
}
 
/**
 * Selects the next player unit in a certain direction.
 * @param dir Direction to select, eg. -1 for previous and 1 for next.
 * @param checkReselect Whether to check if we should reselect a unit.
 * @param setReselect Don't reselect a unit.
 * @param checkInventory Whether to check if the unit has an inventory.
 * @return Pointer to new selected BattleUnit, NULL if none can be selected.
 */
BattleUnit *SavedBattleGame::selectPlayerUnit(int dir, bool checkReselect, bool setReselect, bool checkInventory)
{
	if (_selectedUnit != 0 && setReselect)
	{
		_selectedUnit->dontReselect();
	}
	if (_units.empty())
	{
		return 0;
	}
 
	std::vector<BattleUnit*>::iterator begin, end;
	if (dir > 0)
	{
		begin = _units.begin();
		end = _units.end()-1;
	}
	else if (dir < 0)
	{
		begin = _units.end()-1;
		end = _units.begin();
	}
 
	std::vector<BattleUnit*>::iterator i = std::find(_units.begin(), _units.end(), _selectedUnit);
	do
	{
		// no unit selected
		if (i == _units.end())
		{
			i = begin;
			continue;
		}
		if (i != end)
		{
			i += dir;
		}
		// reached the end, wrap-around
		else
		{
			i = begin;
		}
		// back to where we started... no more units found
		if (*i == _selectedUnit)
		{
			if (checkReselect && !_selectedUnit->reselectAllowed())
				_selectedUnit = 0;
			return _selectedUnit;
		}
		else if (_selectedUnit == 0 && i == begin)
		{
			return _selectedUnit;
		}
	}
	while (!(*i)->isSelectable(_side, checkReselect, checkInventory));
 
	_selectedUnit = (*i);
	return _selectedUnit;
}
 
/**
 * Selects the unit at the given position on the map.
 * @param pos Position.
 * @return Pointer to a BattleUnit, or 0 when none is found.
 */
BattleUnit *SavedBattleGame::selectUnit(Position pos)
{
	BattleUnit *bu = getTile(pos)->getUnit();
 
	if (bu && bu->isOut())
	{
		return 0;
	}
	else
	{
		return bu;
	}
}
 
/**
 * Gets the list of nodes.
 * @return Pointer to the list of nodes.
 */
std::vector<Node*> *SavedBattleGame::getNodes()
{
	return &_nodes;
}
 
/**
 * Gets the list of units.
 * @return Pointer to the list of units.
 */
std::vector<BattleUnit*> *SavedBattleGame::getUnits()
{
	return &_units;
}
 
/**
 * Gets the list of items.
 * @return Pointer to the list of items.
 */
std::vector<BattleItem*> *SavedBattleGame::getItems()
{
	return &_items;
}
 
/**
 * Gets the pathfinding object.
 * @return Pointer to the pathfinding object.
 */
Pathfinding *SavedBattleGame::getPathfinding() const
{
	return _pathfinding;
}
 
/**
 * Gets the terrain modifier object.
 * @return Pointer to the terrain modifier object.
 */
TileEngine *SavedBattleGame::getTileEngine() const
{
	return _tileEngine;
}
 
/**
 * Gets the array of mapblocks.
 * @return Pointer to the array of mapblocks.
 */
std::vector<MapDataSet*> *SavedBattleGame::getMapDataSets()
{
	return &_mapDataSets;
}
 
/**
 * Gets the side currently playing.
 * @return The unit faction currently playing.
 */
UnitFaction SavedBattleGame::getSide() const
{
	return _side;
}
 
/**
 * Gets the current turn number.
 * @return The current turn.
 */
int SavedBattleGame::getTurn() const
{
	return _turn;
}
 
/**
 * Ends the current turn and progresses to the next one.
 */
void SavedBattleGame::endTurn()
{
	if (_side == FACTION_PLAYER)
	{
		if (_selectedUnit && _selectedUnit->getOriginalFaction() == FACTION_PLAYER)
			_lastSelectedUnit = _selectedUnit;
		_selectedUnit =  0;
		_side = FACTION_HOSTILE;
	}
	else if (_side == FACTION_HOSTILE)
	{
		_side = FACTION_NEUTRAL;
		// if there is no neutral team, we skip this and instantly prepare the new turn for the player
		if (selectNextPlayerUnit() == 0)
		{
			prepareNewTurn();
			_turn++;
			_side = FACTION_PLAYER;
			if (_lastSelectedUnit && _lastSelectedUnit->isSelectable(FACTION_PLAYER, false, false))
				_selectedUnit = _lastSelectedUnit;
			else
				selectNextPlayerUnit();
			while (_selectedUnit && _selectedUnit->getFaction() != FACTION_PLAYER)
				selectNextPlayerUnit();
		}
 
	}
	else if (_side == FACTION_NEUTRAL)
	{
		prepareNewTurn();
		_turn++;
		_side = FACTION_PLAYER;
		if (_lastSelectedUnit && _lastSelectedUnit->isSelectable(FACTION_PLAYER, false, false))
			_selectedUnit = _lastSelectedUnit;
		else
			selectNextPlayerUnit();
		while (_selectedUnit && _selectedUnit->getFaction() != FACTION_PLAYER)
			selectNextPlayerUnit();
	}
	int liveSoldiers, liveAliens;
 
	_battleState->getBattleGame()->tallyUnits(liveAliens, liveSoldiers);
 
	if ((_turn > _cheatTurn / 2 && liveAliens <= 2) || _turn > _cheatTurn)
	{
		_cheating = true;
	}
 
	if (_side == FACTION_PLAYER)
	{
		// update the "number of turns since last spotted"
		for (std::vector<BattleUnit*>::iterator i = _units.begin(); i != _units.end(); ++i)
		{
			if ((*i)->getTurnsSinceSpotted() < 255)
			{
				(*i)->setTurnsSinceSpotted((*i)->getTurnsSinceSpotted() +	1);
			}
			if (_cheating && (*i)->getFaction() == FACTION_PLAYER && !(*i)->isOut())
			{
				(*i)->setTurnsSinceSpotted(0);
			}
			if ((*i)->getAIModule())
			{
				(*i)->getAIModule()->reset(); // clean up AI state
			}
		}
	}
	// hide all aliens (VOF calculations below will turn them visible again)
	for (std::vector<BattleUnit*>::iterator i = _units.begin(); i != _units.end(); ++i)
	{
		if ((*i)->getFaction() == _side)
		{
			(*i)->prepareNewTurn();
		}
		if ((*i)->getFaction() != FACTION_PLAYER)
		{
			(*i)->setVisible(false);
		}
	}
 
	// re-run calculateFOV() *after* all aliens have been set not-visible
	_tileEngine->recalculateFOV();
 
	if (_side != FACTION_PLAYER)
		selectNextPlayerUnit();
}
 
/**
 * Turns on debug mode.
 */
void SavedBattleGame::setDebugMode()
{
	for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
	{
		_tiles[i]->setDiscovered(true, 2);
	}
 
	_debugMode = true;
}
 
/**
 * Gets the current debug mode.
 * @return Debug mode.
 */
bool SavedBattleGame::getDebugMode() const
{
	return _debugMode;
}
 
/**
 * Gets the BattlescapeState.
 * @return Pointer to the BattlescapeState.
 */
BattlescapeState *SavedBattleGame::getBattleState()
{
	return _battleState;
}
 
/**
 * Gets the BattlescapeState.
 * @return Pointer to the BattlescapeState.
 */
BattlescapeGame *SavedBattleGame::getBattleGame()
{
	return _battleState->getBattleGame();
}
 
/**
 * Sets the BattlescapeState.
 * @param bs A Pointer to a BattlescapeState.
 */
void SavedBattleGame::setBattleState(BattlescapeState *bs)
{
	_battleState = bs;
}
 
/**
 * Resets all the units to their current standing tile(s).
 */
void SavedBattleGame::resetUnitTiles()
{
	for (std::vector<BattleUnit*>::iterator i = _units.begin(); i != _units.end(); ++i)
	{
		if (!(*i)->isOut())
		{
			int size = (*i)->getArmor()->getSize() - 1;
			if ((*i)->getTile() && (*i)->getTile()->getUnit() == (*i))
			{
				for (int x = size; x >= 0; x--)
				{
					for (int y = size; y >= 0; y--)
					{
						getTile((*i)->getTile()->getPosition() + Position(x,y,0))->setUnit(0);
					}
				}
			}
			for (int x = size; x >= 0; x--)
			{
				for (int y = size; y >= 0; y--)
				{
					Tile *t = getTile((*i)->getPosition() + Position(x,y,0));
					t->setUnit((*i), getTile(t->getPosition() + Position(0,0,-1)));
				}
			}
 
		}
		if ((*i)->getFaction() == FACTION_PLAYER)
		{
			(*i)->setVisible(true);
		}
	}
	_beforeGame = false;
}
 
/**
 * Gives access to the "storage space" vector, for distribution of items in base defense missions.
 * @return Vector of storage positions.
 */
std::vector<Position> &SavedBattleGame::getStorageSpace()
{
	return _storageSpace;
}
 
/**
 * Move all the leftover items in base defense missions to random locations in the storage facilities
 * @param t the tile where all our goodies are initially stored.
 */
void SavedBattleGame::randomizeItemLocations(Tile *t)
{
	if (!_storageSpace.empty())
	{
		for (std::vector<BattleItem*>::iterator it = t->getInventory()->begin(); it != t->getInventory()->end();)
		{
			if ((*it)->getSlot()->getId() == "STR_GROUND")
			{
				getTile(_storageSpace.at(RNG::generate(0, _storageSpace.size() -1)))->addItem(*it, (*it)->getSlot());
				it = t->getInventory()->erase(it);
			}
			else
			{
				++it;
			}
		}
	}
}
 
/**
 * Removes an item from the game. Eg. when ammo item is depleted.
 * @param item The Item to remove.
 */
void SavedBattleGame::removeItem(BattleItem *item)
{
	// only delete once
	for (std::vector<BattleItem*>::iterator it = _deleted.begin(); it != _deleted.end(); ++it)
	{
		if ((*it) == item)
		{
			return;
		}
	}
 
	// due to strange design, the item has to be removed from the tile it is on too (if it is on a tile)
	Tile *t = item->getTile();
	BattleUnit *b = item->getOwner();
	if (t)
	{
		for (std::vector<BattleItem*>::iterator it = t->getInventory()->begin(); it != t->getInventory()->end(); ++it)
		{
			if ((*it) == item)
			{
				t->getInventory()->erase(it);
				break;
			}
		}
	}
	if (b)
	{
		for (std::vector<BattleItem*>::iterator it = b->getInventory()->begin(); it != b->getInventory()->end(); ++it)
		{
			if ((*it) == item)
			{
				b->getInventory()->erase(it);
				break;
			}
		}
	}
 
	for (std::vector<BattleItem*>::iterator i = _items.begin(); i != _items.end(); ++i)
	{
		if (*i == item)
		{
			_items.erase(i);
			break;
		}
	}
 
	_deleted.push_back(item);
	/*
	for (int i = 0; i < _mapsize_x * _mapsize_y * _mapsize_z; ++i)
	{
		for (std::vector<BattleItem*>::iterator it = _tiles[i]->getInventory()->begin(); it != _tiles[i]->getInventory()->end(); )
		{
			if ((*it) == item)
			{
				it = _tiles[i]->getInventory()->erase(it);
				return;
			}
			++it;
		}
	}
	*/
 
}
 
/**
 * Converts a unit into a unit of another type.
 * @param unit The unit to convert.
 * @return Pointer to the new unit.
 */
BattleUnit *SavedBattleGame::convertUnit(BattleUnit *unit, const SavedGame* saveGame, Mod* mod)
{
	const std::string newType = unit->getSpawnUnit();
	bool visible = unit->getVisible();
	// in case the unit was unconscious
	removeUnconsciousBodyItem(unit);
 
	unit->instaKill();
 
	for (std::vector<BattleItem*>::iterator i = unit->getInventory()->begin(); i != unit->getInventory()->end(); ++i)
	{
		getTileEngine()->itemDrop(getTile(unit->getPosition()), (*i), mod);
		(*i)->setOwner(0);
	}
 
	unit->getInventory()->clear();
 
	// remove unit-tile link
	unit->setTile(0);
 
	getTile(unit->getPosition())->setUnit(0);
	Unit *newRule = mod->getUnit(newType, true);
	std::string newArmor = newRule->getArmor();
	std::string terroristWeapon = newRule->getRace().substr(4);
	terroristWeapon += "_WEAPON";
	RuleItem *newItem = mod->getItem(terroristWeapon);
 
	BattleUnit *newUnit = new BattleUnit(newRule,
		FACTION_HOSTILE,
		getUnits()->back()->getId() + 1,
		mod->getArmor(newArmor, true),
		mod->getStatAdjustment(saveGame->getDifficulty()),
		getDepth());
 
	getTile(unit->getPosition())->setUnit(newUnit, getTile(unit->getPosition() + Position(0,0,-1)));
	newUnit->setPosition(unit->getPosition());
	newUnit->setDirection(unit->getDirection());
	newUnit->setCache(0);
	newUnit->setTimeUnits(0);
	newUnit->setSpecialWeapon(this, mod);
	getUnits()->push_back(newUnit);
	newUnit->setAIModule(new AIModule(this, newUnit, 0));
	if (newItem)
	{
		BattleItem *bi = new BattleItem(newItem, getCurrentItemId());
		bi->moveToOwner(newUnit);
		bi->setSlot(mod->getInventory("STR_RIGHT_HAND", true));
		getItems()->push_back(bi);
	}
	newUnit->setVisible(visible);
	getTileEngine()->calculateFOV(newUnit->getPosition());
	getTileEngine()->applyGravity(newUnit->getTile());
	newUnit->dontReselect();
	return newUnit;
}
 
 
/**
 * Sets whether the mission was aborted or successful.
 * @param flag True, if the mission was aborted, or false, if the mission was successful.
 */
void SavedBattleGame::setAborted(bool flag)
{
	_aborted = flag;
}
 
/**
 * Returns whether the mission was aborted or successful.
 * @return True, if the mission was aborted, or false, if the mission was successful.
 */
bool SavedBattleGame::isAborted() const
{
	return _aborted;
}
 
/**
 * increments the number of objectives to be destroyed.
 */
void SavedBattleGame::setObjectiveCount(int counter)
{
	_objectivesNeeded = counter;
	_objectivesDestroyed = 0;
}
 
/**
 * Sets whether the objective is destroyed.
 */
void SavedBattleGame::addDestroyedObjective()
{
	if (!allObjectivesDestroyed())
	{
		_objectivesDestroyed++;
		if (allObjectivesDestroyed())
		{
			if (getObjectiveType() == MUST_DESTROY)
			{
				_battleState->getBattleGame()->autoEndBattle();
			}
			else
			{
				_battleState->getBattleGame()->missionComplete();
			}
		}
	}
}
 
/**
 * Returns whether the objectives are destroyed.
 * @return True if the objectives are destroyed.
 */
bool SavedBattleGame::allObjectivesDestroyed() const
{
	return (_objectivesNeeded > 0 && _objectivesDestroyed == _objectivesNeeded);
}
 
/**
 * Gets the current item ID.
 * @return Current item ID pointer.
 */
int *SavedBattleGame::getCurrentItemId()
{
	return &_itemId;
}
 
/**
 * Finds a fitting node where a unit can spawn.
 * @param nodeRank Rank of the node (this is not the rank of the alien!).
 * @param unit Pointer to the unit (to get its position).
 * @return Pointer to the chosen node.
 */
Node *SavedBattleGame::getSpawnNode(int nodeRank, BattleUnit *unit)
{
	int highestPriority = -1;
	std::vector<Node*> compliantNodes;
 
	for (std::vector<Node*>::iterator i = getNodes()->begin(); i != getNodes()->end(); ++i)
	{
		if ((*i)->isDummy())
		{
			continue;
		}
		if ((*i)->getRank() == nodeRank								// ranks must match
			&& (!((*i)->getType() & Node::TYPE_SMALL)
				|| unit->getArmor()->getSize() == 1)				// the small unit bit is not set or the unit is small
			&& (!((*i)->getType() & Node::TYPE_FLYING)
				|| unit->getMovementType() == MT_FLY)				// the flying unit bit is not set or the unit can fly
			&& (*i)->getPriority() > 0								// priority 0 is no spawnplace
			&& setUnitPosition(unit, (*i)->getPosition(), true))	// check if not already occupied
		{
			if ((*i)->getPriority() > highestPriority)
			{
				highestPriority = (*i)->getPriority();
				compliantNodes.clear(); // drop the last nodes, as we found a higher priority now
			}
			if ((*i)->getPriority() == highestPriority)
			{
				compliantNodes.push_back((*i));
			}
		}
	}
 
	if (compliantNodes.empty()) return 0;
 
	int n = RNG::generate(0, compliantNodes.size() - 1);
 
	return compliantNodes[n];
}
 
/**
 * Finds a fitting node where a unit can patrol to.
 * @param scout Is the unit scouting?
 * @param unit Pointer to the unit (to get its position).
 * @param fromNode Pointer to the node the unit is at.
 * @return Pointer to the chosen node.
 */
Node *SavedBattleGame::getPatrolNode(bool scout, BattleUnit *unit, Node *fromNode)
{
	std::vector<Node *> compliantNodes;
	Node *preferred = 0;
 
	if (fromNode == 0)
	{
		if (Options::traceAI) { Log(LOG_INFO) << "This alien got lost. :("; }
		fromNode = getNodes()->at(RNG::generate(0, getNodes()->size() - 1));
		while (fromNode->isDummy())
		{
			fromNode = getNodes()->at(RNG::generate(0, getNodes()->size() - 1));
		}
	}
 
	// scouts roam all over while all others shuffle around to adjacent nodes at most:
	const int end = scout ? getNodes()->size() : fromNode->getNodeLinks()->size();
 
	for (int i = 0; i < end; ++i)
	{
		if (!scout && fromNode->getNodeLinks()->at(i) < 1) continue;
 
		Node *n = getNodes()->at(scout ? i : fromNode->getNodeLinks()->at(i));
		if ( !n->isDummy()																				// don't consider dummy nodes.
			&& (n->getFlags() > 0 || n->getRank() > 0 || scout)											// for non-scouts we find a node with a desirability above 0
			&& (!(n->getType() & Node::TYPE_SMALL) || unit->getArmor()->getSize() == 1)					// the small unit bit is not set or the unit is small
			&& (!(n->getType() & Node::TYPE_FLYING) || unit->getMovementType() == MT_FLY)				// the flying unit bit is not set or the unit can fly
			&& !n->isAllocated()																		// check if not allocated
			&& !(n->getType() & Node::TYPE_DANGEROUS)													// don't go there if an alien got shot there; stupid behavior like that
			&& setUnitPosition(unit, n->getPosition(), true)											// check if not already occupied
			&& getTile(n->getPosition()) && !getTile(n->getPosition())->getFire()						// you are not a firefighter; do not patrol into fire
			&& (unit->getFaction() != FACTION_HOSTILE || !getTile(n->getPosition())->getDangerous())	// aliens don't run into a grenade blast
			&& (!scout || n != fromNode)																// scouts push forward
			&& n->getPosition().x > 0 && n->getPosition().y > 0)
		{
			if (!preferred
				|| (unit->getRankInt() >=0 &&
					preferred->getRank() == Node::nodeRank[unit->getRankInt()][0] &&
					preferred->getFlags() < n->getFlags())
				|| preferred->getFlags() < n->getFlags())
			{
				preferred = n;
			}
			compliantNodes.push_back(n);
		}
	}
 
	if (compliantNodes.empty())
	{
		if (Options::traceAI) { Log(LOG_INFO) << (scout ? "Scout " : "Guard") << " found on patrol node! XXX XXX XXX"; }
		if (unit->getArmor()->getSize() > 1 && !scout)
		{
			return getPatrolNode(true, unit, fromNode); // move dammit
		}
		else
			return 0;
	}
 
	if (scout)
	{
		// scout picks a random destination:
		return compliantNodes[RNG::generate(0, compliantNodes.size() - 1)];
	}
	else
	{
		if (!preferred) return 0;
 
		// non-scout patrols to highest value unoccupied node that's not fromNode
		if (Options::traceAI) { Log(LOG_INFO) << "Choosing node flagged " << preferred->getFlags(); }
		return preferred;
	}
}
 
/**
 * Carries out new turn preparations such as fire and smoke spreading.
 */
void SavedBattleGame::prepareNewTurn()
{
	std::vector<Tile*> tilesOnFire;
	std::vector<Tile*> tilesOnSmoke;
 
	// prepare a list of tiles on fire
	for (int i = 0; i < _mapsize_x * _mapsize_y * _mapsize_z; ++i)
	{
		if (getTiles()[i]->getFire() > 0)
		{
			tilesOnFire.push_back(getTiles()[i]);
		}
	}
 
	// first: fires spread
	for (std::vector<Tile*>::iterator i = tilesOnFire.begin(); i != tilesOnFire.end(); ++i)
	{
		// if we haven't added fire here this turn
		if ((*i)->getOverlaps() == 0)
		{
			// reduce the fire timer
			(*i)->setFire((*i)->getFire() -1);
 
			// if we're still burning
			if ((*i)->getFire())
			{
				// propagate in four cardinal directions (0, 2, 4, 6)
				for (int dir = 0; dir <= 6; dir += 2)
				{
					Position pos;
					Pathfinding::directionToVector(dir, &pos);
					Tile *t = getTile((*i)->getPosition() + pos);
					// if there's no wall blocking the path of the flames...
					if (t && getTileEngine()->horizontalBlockage((*i), t, DT_IN) == 0)
					{
						// attempt to set this tile on fire
						t->ignite((*i)->getSmoke());
					}
				}
			}
			// fire has burnt out
			else
			{
				(*i)->setSmoke(0);
				// burn this tile, and any object in it, if it's not fireproof/indestructible.
				if ((*i)->getMapData(O_OBJECT))
				{
					if ((*i)->getMapData(O_OBJECT)->getFlammable() != 255 && (*i)->getMapData(O_OBJECT)->getArmor() != 255)
					{
						if ((*i)->destroy(O_OBJECT, getObjectiveType()))
						{
							addDestroyedObjective();
						}
						if ((*i)->destroy(O_FLOOR, getObjectiveType()))
						{
							addDestroyedObjective();
						}
					}
				}
				else if ((*i)->getMapData(O_FLOOR))
				{
					if ((*i)->getMapData(O_FLOOR)->getFlammable() != 255 && (*i)->getMapData(O_FLOOR)->getArmor() != 255)
					{
						if ((*i)->destroy(O_FLOOR, getObjectiveType()))
						{
							addDestroyedObjective();
						}
					}
				}
				getTileEngine()->applyGravity(*i);
			}
		}
	}
 
	// prepare a list of tiles on fire/with smoke in them (smoke acts as fire intensity)
	for (int i = 0; i < _mapsize_x * _mapsize_y * _mapsize_z; ++i)
	{
		if (getTiles()[i]->getSmoke() > 0)
		{
			tilesOnSmoke.push_back(getTiles()[i]);
		}
		getTiles()[i]->setDangerous(false);
	}
 
	// now make the smoke spread.
	for (std::vector<Tile*>::iterator i = tilesOnSmoke.begin(); i != tilesOnSmoke.end(); ++i)
	{
		// smoke and fire follow slightly different rules.
		if ((*i)->getFire() == 0)
		{
			// reduce the smoke counter
			(*i)->setSmoke((*i)->getSmoke() - 1);
			// if we're still smoking
			if ((*i)->getSmoke())
			{
				// spread in four cardinal directions
				for (int dir = 0; dir <= 6; dir += 2)
				{
					Position pos;
					Pathfinding::directionToVector(dir, &pos);
					Tile *t = getTile((*i)->getPosition() + pos);
					// as long as there are no walls blocking us
					if (t && getTileEngine()->horizontalBlockage((*i), t, DT_SMOKE) == 0)
					{
						// only add smoke to empty tiles, or tiles with no fire, and smoke that was added this turn
						if (t->getSmoke() == 0 || (t->getFire() == 0 && t->getOverlaps() != 0))
						{
							t->addSmoke((*i)->getSmoke());
						}
					}
				}
			}
		}
		else
		{
			// smoke from fire spreads upwards one level if there's no floor blocking it.
			Position pos = Position(0,0,1);
			Tile *t = getTile((*i)->getPosition() + pos);
			if (t && t->hasNoFloor(*i))
			{
				// only add smoke equal to half the intensity of the fire
				t->addSmoke((*i)->getSmoke()/2);
			}
			// then it spreads in the four cardinal directions.
			for (int dir = 0; dir <= 6; dir += 2)
			{
				Pathfinding::directionToVector(dir, &pos);
				t = getTile((*i)->getPosition() + pos);
				if (t && getTileEngine()->horizontalBlockage((*i), t, DT_SMOKE) == 0)
				{
					t->addSmoke((*i)->getSmoke()/2);
				}
			}
		}
	}
 
	if (!tilesOnFire.empty() || !tilesOnSmoke.empty())
	{
		// do damage to units, average out the smoke, etc.
		for (int i = 0; i < _mapsize_x * _mapsize_y * _mapsize_z; ++i)
		{
			if (getTiles()[i]->getSmoke() != 0)
				getTiles()[i]->prepareNewTurn(getDepth() == 0);
		}
		// fires could have been started, stopped or smoke could reveal/conceal units.
		getTileEngine()->calculateTerrainLighting();
	}
 
	reviveUnconsciousUnits();
}
 
/**
 * Checks for units that are unconscious and revives them if they shouldn't be.
 *
 * Revived units need a tile to stand on. If the unit's current position is occupied, then
 * all directions around the tile are searched for a free tile to place the unit in.
 * If no free tile is found the unit stays unconscious.
 */
void SavedBattleGame::reviveUnconsciousUnits()
{
	for (std::vector<BattleUnit*>::iterator i = getUnits()->begin(); i != getUnits()->end(); ++i)
	{
		if ((*i)->getArmor()->getSize() == 1)
		{
			Position originalPosition = (*i)->getPosition();
			if (originalPosition == Position(-1, -1, -1))
			{
				for (std::vector<BattleItem*>::iterator j = _items.begin(); j != _items.end(); ++j)
				{
					if ((*j)->getUnit() && (*j)->getUnit() == *i && (*j)->getOwner())
					{
						originalPosition = (*j)->getOwner()->getPosition();
					}
				}
			}
			if ((*i)->getStatus() == STATUS_UNCONSCIOUS && (*i)->getStunlevel() < (*i)->getHealth() && (*i)->getHealth() > 0)
			{
				Tile *targetTile = getTile(originalPosition);
				bool largeUnit =  targetTile && targetTile->getUnit() && targetTile->getUnit() != *i && targetTile->getUnit()->getArmor()->getSize() != 1;
				if (placeUnitNearPosition((*i), originalPosition, largeUnit))
				{
					// recover from unconscious
					(*i)->turn(false); // makes the unit stand up again
					(*i)->kneel(false);
					(*i)->setCache(0);
					getTileEngine()->calculateFOV((*i));
					getTileEngine()->calculateUnitLighting();
					removeUnconsciousBodyItem((*i));
				}
			}
		}
	}
}
 
/**
 * Removes the body item that corresponds to the unit.
 */
void SavedBattleGame::removeUnconsciousBodyItem(BattleUnit *bu)
{
	// remove the unconscious body item corresponding to this unit
	for (std::vector<BattleItem*>::iterator it = getItems()->begin(); it != getItems()->end(); )
	{
		if ((*it)->getUnit() == bu)
		{
			removeItem((*it));
			break;
		}
		++it;
	}
}
 
/**
 * Places units on the map. Handles large units that are placed on multiple tiles.
 * @param bu The unit to be placed.
 * @param position The position to place the unit.
 * @param testOnly If true then just checks if the unit can be placed at the position.
 * @return True if the unit could be successfully placed.
 */
bool SavedBattleGame::setUnitPosition(BattleUnit *bu, Position position, bool testOnly)
{
	int size = bu->getArmor()->getSize() - 1;
	Position zOffset (0,0,0);
	// first check if the tiles are occupied
	for (int x = size; x >= 0; x--)
	{
		for (int y = size; y >= 0; y--)
		{
			Tile *t = getTile(position + Position(x,y,0) + zOffset);
			Tile *tb = getTile(position + Position(x,y,-1) + zOffset);
			if (t == 0 ||
				(t->getUnit() != 0 && t->getUnit() != bu) ||
				t->getTUCost(O_OBJECT, bu->getMovementType()) == 255 ||
				(t->hasNoFloor(tb) && bu->getMovementType() != MT_FLY) ||
				(t->getMapData(O_OBJECT) && t->getMapData(O_OBJECT)->getBigWall() && t->getMapData(O_OBJECT)->getBigWall() <= 3))
			{
				return false;
			}
			// move the unit up to the next level (desert and seabed terrains)
			if (t && t->getTerrainLevel() == -24)
			{
				zOffset.z += 1;
				x = size;
				y = size + 1;
			}
		}
	}
 
	if (size > 0)
	{
		getPathfinding()->setUnit(bu);
		for (int dir = 2; dir <= 4; ++dir)
		{
			if (getPathfinding()->isBlocked(getTile(position + zOffset), 0, dir, 0))
				return false;
		}
	}
 
	if (testOnly) return true;
 
	for (int x = size; x >= 0; x--)
	{
		for (int y = size; y >= 0; y--)
		{
			if (x==0 && y==0)
			{
				bu->setPosition(position + zOffset);
			}
			getTile(position + Position(x,y,0) + zOffset)->setUnit(bu, getTile(position + Position(x,y,-1) + zOffset));
		}
	}
 
	return true;
}
 
/**
 * @brief Checks whether anyone on a particular faction is looking at the unit.
 *
 * Similar to getSpottingUnits() but returns a bool and stops searching if one positive hit is found.
 *
 * @param faction Faction to check through.
 * @param unit Whom to spot.
 * @return True when the unit can be seen
 */
bool SavedBattleGame::eyesOnTarget(UnitFaction faction, BattleUnit* unit)
{
	for (std::vector<BattleUnit*>::iterator i = getUnits()->begin(); i != getUnits()->end(); ++i)
	{
		if ((*i)->getFaction() != faction) continue;
 
		std::vector<BattleUnit*> *vis = (*i)->getVisibleUnits();
		if (std::find(vis->begin(), vis->end(), unit) != vis->end()) return true;
		// aliens know the location of all XCom agents sighted by all other aliens due to sharing locations over their space-walkie-talkies
	}
 
	return false;
}
 
/**
 * Adds this unit to the vector of falling units,
 * if it doesn't already exist.
 * @param unit The unit.
 * @return Was the unit added?
 */
bool SavedBattleGame::addFallingUnit(BattleUnit* unit)
{
	bool add = true;
	for (std::list<BattleUnit*>::iterator i = _fallingUnits.begin(); i != _fallingUnits.end(); ++i)
	{
		if (unit == *i)
		{
			add = false;
			break;
		}
	}
	if (add)
	{
		_fallingUnits.push_front(unit);
		_unitsFalling = true;
	}
	return add;
}
 
/**
 * Gets all units in the battlescape that are falling.
 * @return The falling units in the battlescape.
 */
std::list<BattleUnit*> *SavedBattleGame::getFallingUnits()
{
	return &_fallingUnits;
}
 
/**
 * Toggles the switch that says "there are units falling, start the fall state".
 * @param fall True if there are any units falling in the battlescape.
 */
void SavedBattleGame::setUnitsFalling(bool fall)
{
	_unitsFalling = fall;
}
 
/**
 * Returns whether there are any units falling in the battlescape.
 * @return True if there are any units falling in the battlescape.
 */
bool SavedBattleGame::getUnitsFalling() const
{
	return _unitsFalling;
}
 
/**
 * Gets the highest ranked, living XCom unit.
 * @return The highest ranked, living XCom unit.
 */
BattleUnit* SavedBattleGame::getHighestRankedXCom()
{
	BattleUnit* highest = 0;
	for (std::vector<BattleUnit*>::iterator j = _units.begin(); j != _units.end(); ++j)
	{
		if ((*j)->getOriginalFaction() == FACTION_PLAYER && !(*j)->isOut())
		{
			if (highest == 0 || (*j)->getRankInt() > highest->getRankInt())
			{
				highest = *j;
			}
		}
	}
	return highest;
}
 
/**
 * Gets the morale modifier for
 * - either XCom based on the highest ranked, living XCom unit,
 * - or the unit passed to this function.
 * @param unit Unit.
 * @return The morale modifier.
 */
int SavedBattleGame::getMoraleModifier(BattleUnit* unit)
{
	int result = 100;
 
	if (unit == 0)
	{
		BattleUnit *leader = getHighestRankedXCom();
		if (leader)
		{
			switch (leader->getRankInt())
			{
			case 5:
				result += 25;
			case 4:
				result += 10;
			case 3:
				result += 5;
			case 2:
				result += 10;
			default:
				break;
			}
		}
	}
	else if (unit->getFaction() == FACTION_PLAYER)
	{
		switch (unit->getRankInt())
		{
		case 5:
			result += 25;
		case 4:
			result += 20;
		case 3:
			result += 10;
		case 2:
			result += 20;
		default:
			break;
		}
	}
	return result;
}
 
/**
 * Places a unit on or near a position.
 * @param unit The unit to place.
 * @param entryPoint The position around which to attempt to place the unit.
 * @return True if the unit was successfully placed.
 */
bool SavedBattleGame::placeUnitNearPosition(BattleUnit *unit, const Position& entryPoint, bool largeFriend)
{
	if (setUnitPosition(unit, entryPoint))
	{
		return true;
	}
 
	int me = 0 - unit->getArmor()->getSize();
	int you = largeFriend ? 2 : 1;
	int xArray[8] = {0, you, you, you, 0, me, me, me};
	int yArray[8] = {me, me, 0, you, you, you, 0, me};
	for (int dir = 0; dir <= 7; ++dir)
	{
		Position offset = Position (xArray[dir], yArray[dir], 0);
		Tile *t = getTile(entryPoint + offset);
		if (t && !getPathfinding()->isBlocked(getTile(entryPoint + (offset / 2)), t, dir, 0)
			&& setUnitPosition(unit, entryPoint + offset))
		{
			return true;
		}
	}
 
	if (unit->getMovementType() == MT_FLY)
	{
		Tile *t = getTile(entryPoint + Position(0, 0, 1));
		if (t && t->hasNoFloor(getTile(entryPoint)) && setUnitPosition(unit, entryPoint + Position(0, 0, 1)))
		{
			return true;
		}
	}
	return false;
}
 
/**
 * Resets the turn counter.
 */
void SavedBattleGame::resetTurnCounter()
{
	_turn = 1;
	_cheating = false;
	_side = FACTION_PLAYER;
	_beforeGame = true;
}
 
/**
 * Resets visibility of all the tiles on the map.
 */
void SavedBattleGame::resetTiles()
{
	for (int i = 0; i != getMapSizeXYZ(); ++i)
	{
		_tiles[i]->setDiscovered(false, 0);
		_tiles[i]->setDiscovered(false, 1);
		_tiles[i]->setDiscovered(false, 2);
	}
}
 
/**
 * @return the tilesearch vector for use in AI functions.
 */
const std::vector<Position> &SavedBattleGame::getTileSearch() const
{
	return _tileSearch;
}
 
/**
 * is the AI allowed to cheat?
 * @return true if cheating.
 */
bool SavedBattleGame::isCheating() const
{
	return _cheating;
}
 
/**
 * Gets the TU reserved type.
 * @return A battleactiontype.
 */
BattleActionType SavedBattleGame::getTUReserved() const
{
	return _tuReserved;
}
 
/**
 * Sets the TU reserved type.
 * @param reserved A battleactiontype.
 */
void SavedBattleGame::setTUReserved(BattleActionType reserved)
{
	_tuReserved = reserved;
}
 
/**
 * Gets the kneel reservation setting.
 * @return Should we reserve an extra 4 TUs to kneel?
 */
bool SavedBattleGame::getKneelReserved() const
{
	return _kneelReserved;
}
 
/**
 * Sets the kneel reservation setting.
 * @param reserved Should we reserve an extra 4 TUs to kneel?
 */
void SavedBattleGame::setKneelReserved(bool reserved)
{
	_kneelReserved = reserved;
}
 
/**
 * Return a reference to the base module destruction map
 * this map contains information on how many destructible base modules
 * remain at any given grid reference in the basescape, using [x][y] format.
 * -1 for "no items" 0 for "destroyed" and any actual number represents how many left.
 * @return the base module damage map.
 */
std::vector< std::vector<std::pair<int, int> > > &SavedBattleGame::getModuleMap()
{
	return _baseModules;
}
 
/**
 * calculate the number of map modules remaining by counting the map objects
 * on the top floor who have the baseModule flag set. we store this data in the grid
 * as outlined in the comments above, in pairs representing initial and current values.
 */
void SavedBattleGame::calculateModuleMap()
{
	_baseModules.resize((_mapsize_x / 10), std::vector<std::pair<int, int> >((_mapsize_y / 10), std::make_pair(-1, -1)));
 
	for (int x = 0; x != _mapsize_x; ++x)
	{
		for (int y = 0; y != _mapsize_y; ++y)
		{
			for (int z = 0; z != _mapsize_z; ++z)
			{
				Tile *tile = getTile(Position(x,y,z));
				if (tile && tile->getMapData(O_OBJECT) && tile->getMapData(O_OBJECT)->isBaseModule())
				{
					_baseModules[x/10][y/10].first += _baseModules[x/10][y/10].first > 0 ? 1 : 2;
					_baseModules[x/10][y/10].second = _baseModules[x/10][y/10].first;
				}
			}
		}
	}
}
 
/**
 * get a pointer to the geoscape save
 * @return a pointer to the geoscape save.
 */
SavedGame *SavedBattleGame::getGeoscapeSave()
{
	return _battleState->getGame()->getSavedGame();
}
 
/**
 * check the depth of the battlescape.
 * @return depth.
 */
int SavedBattleGame::getDepth() const
{
	return _depth;
}
 
/**
 * set the depth of the battlescape game.
 * @param depth the intended depth 0-3.
 */
void SavedBattleGame::setDepth(int depth)
{
	_depth = depth;
}
 
/**
 * uses the depth variable to choose a palette.
 * @param state the state to set the palette for.
 */
void SavedBattleGame::setPaletteByDepth(State *state)
{
	if (_depth == 0)
	{
		state->setPalette("PAL_BATTLESCAPE");
	}
	else
	{
		std::ostringstream ss;
		ss << "PAL_BATTLESCAPE_" << _depth;
		state->setPalette(ss.str());
	}
}
 
/**
 * set the ambient battlescape sound effect.
 * @param sound the intended sound.
 */
void SavedBattleGame::setAmbientSound(int sound)
{
	_ambience = sound;
}
 
/**
 * get the ambient battlescape sound effect.
 * @return the intended sound.
 */
int SavedBattleGame::getAmbientSound() const
{
	return _ambience;
}
 
/**
 * get the list of items we're guaranteed to take with us (ie: items that were in the skyranger)
 * @return the list of items we're guaranteed.
 */
std::vector<BattleItem*> *SavedBattleGame::getGuaranteedRecoveredItems()
{
	return &_recoverGuaranteed;
}
 
/**
 * get the list of items we're not guaranteed to take with us (ie: items that were NOT in the skyranger)
 * @return the list of items we might get.
 */
std::vector<BattleItem*> *SavedBattleGame::getConditionalRecoveredItems()
{
	return &_recoverConditional;
}
 
/**
 * Get the music track for the current battle.
 * @return the name of the music track.
 */
std::string &SavedBattleGame::getMusic()
{
	return _music;
}
 
/**
 * Set the music track for this battle.
 * @param track the track name.
 */
void SavedBattleGame::setMusic(const std::string& track)
{
	_music = track;
}
 
/**
 * Set the objective type for the current battle.
 * @param the objective type.
 */
void SavedBattleGame::setObjectiveType(int type)
{
	_objectiveType = type;
}
 
/**
 * Get the objective type for the current battle.
 * @return the objective type.
 */
SpecialTileType SavedBattleGame::getObjectiveType() const
{
	return (SpecialTileType)(_objectiveType);
}
 
 
 
/**
 * Sets the ambient sound effect volume.
 * @param volume the ambient volume.
 */
void SavedBattleGame::setAmbientVolume(double volume)
{
	_ambientVolume = volume;
}
 
/**
 * Gets the ambient sound effect volume.
 * @return the ambient sound volume.
 */
double SavedBattleGame::getAmbientVolume() const
{
	return _ambientVolume;
}
 
/**
 * Gets the maximum number of turns we have before this mission ends.
 * @return the turn limit.
 */
int SavedBattleGame::getTurnLimit() const
{
	return _turnLimit;
}
 
/**
 * Gets the action type to perform when the timer expires.
 * @return the action type to perform.
 */
ChronoTrigger SavedBattleGame::getChronoTrigger() const
{
	return _chronoTrigger;
}
 
/**
 * Sets the turn limit for this mission.
 * @param limit the turn limit.
 */
void SavedBattleGame::setTurnLimit(int limit)
{
	_turnLimit = limit;
}
 
/**
 * Sets the action type to occur when the timer runs out.
 * @param trigger the action type to perform.
 */
void SavedBattleGame::setChronoTrigger(ChronoTrigger trigger)
{
	_chronoTrigger = trigger;
}
 
/**
 * Sets the turn at which the players become exposed to the AI.
 * @param turn the turn to start cheating.
 */
void SavedBattleGame::setCheatTurn(int turn)
{
	_cheatTurn = turn;
}
 
bool SavedBattleGame::isBeforeGame() const
{
	return _beforeGame;
}
 
/**
 * Checks if an item can be used in the current battlescape conditions.
 * @return Error string if it can't be used, "" otherwise.
 */
std::string SavedBattleGame::getItemUsable(BattleItem *item) const
{
	if (_depth == 0 &&
		(item->getRules()->isWaterOnly() ||
		(item->getAmmoItem() != 0 && item->getAmmoItem()->getRules()->isWaterOnly())))
	{
		return "STR_UNDERWATER_EQUIPMENT";
	}
	if (_depth != 0 &&
		(item->getRules()->isLandOnly() ||
		(item->getAmmoItem() != 0 && item->getAmmoItem()->getRules()->isLandOnly())))
	{
		return "STR_LAND_EQUIPMENT";
	}
	return "";
}
 
/**
 * Checks if an item can be used in the current battlescape conditions.
 * @return True if it's usable, False otherwise.
 */
bool SavedBattleGame::isItemUsable(BattleItem *item) const
{
	return getItemUsable(item).empty();
}
 
/**
 * Resets all unit hit state flags.
 */
void SavedBattleGame::resetUnitHitStates()
{
	for (std::vector<BattleUnit*>::iterator i = _units.begin(); i != _units.end(); ++i)
	{
		(*i)->resetHitState();
	}
}
 
}

V807 Decreased performance. Consider creating a pointer to avoid using the 't->getInventory()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the 't->getInventory()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the 'b->getInventory()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the 'unit->getInventory()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the 'fromNode->getNodeLinks()' expression repeatedly.