1 /*
2  * Copyright 2010-2014 OpenXcom Developers.
3  *
4  * This file is part of OpenXcom.
5  *
6  * OpenXcom is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * OpenXcom is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with OpenXcom.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include <assert.h>
20 #include <vector>
21 #include <deque>
22 #include <queue>
23 #include "BattleItem.h"
24 #include "SavedBattleGame.h"
25 #include "SavedGame.h"
26 #include "Tile.h"
27 #include "Node.h"
28 #include "../Ruleset/MapDataSet.h"
29 #include "../Ruleset/MCDPatch.h"
30 #include "../Battlescape/Pathfinding.h"
31 #include "../Battlescape/TileEngine.h"
32 #include "../Battlescape/BattlescapeState.h"
33 #include "../Battlescape/BattlescapeGame.h"
34 #include "../Battlescape/Position.h"
35 #include "../Resource/ResourcePack.h"
36 #include "../Ruleset/Ruleset.h"
37 #include "../Ruleset/Armor.h"
38 #include "../Engine/Language.h"
39 #include "../Engine/Game.h"
40 #include "../Ruleset/RuleInventory.h"
41 #include "../Battlescape/CivilianBAIState.h"
42 #include "../Battlescape/AlienBAIState.h"
43 #include "../Engine/RNG.h"
44 #include "../Engine/Options.h"
45 #include "../Engine/Logger.h"
46 #include "SerializationHelper.h"
47 
48 namespace OpenXcom
49 {
50 
51 /**
52  * Initializes a brand new battlescape saved game.
53  */
SavedBattleGame()54 SavedBattleGame::SavedBattleGame() : _battleState(0), _mapsize_x(0), _mapsize_y(0),
55                                      _mapsize_z(0),   _tiles(), _selectedUnit(0),
56                                      _lastSelectedUnit(0), _nodes(), _units(),
57                                      _items(), _pathfinding(0), _tileEngine(0),
58                                      _missionType(""), _globalShade(0), _side(FACTION_PLAYER),
59                                      _turn(1), _debugMode(false), _aborted(false),
60                                      _itemId(0), _objectiveDestroyed(false), _fallingUnits(),
61                                      _unitsFalling(false), _cheating(false),
62 									 _tuReserved(BA_NONE), _kneelReserved(false)
63 {
64 	_tileSearch.resize(11*11);
65 	for (int i = 0; i < 121; ++i)
66 	{
67 		_tileSearch[i].x = ((i%11) - 5);
68 		_tileSearch[i].y = ((i/11) - 5);
69 	}
70 }
71 
72 /**
73  * Deletes the game content from memory.
74  */
~SavedBattleGame()75 SavedBattleGame::~SavedBattleGame()
76 {
77 	for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
78 	{
79 		delete _tiles[i];
80 	}
81 	delete[] _tiles;
82 
83 	for (std::vector<MapDataSet*>::iterator i = _mapDataSets.begin(); i != _mapDataSets.end(); ++i)
84 	{
85 		(*i)->unloadData();
86 	}
87 
88 	for (std::vector<Node*>::iterator i = _nodes.begin(); i != _nodes.end(); ++i)
89 	{
90 		delete *i;
91 	}
92 
93 	for (std::vector<BattleUnit*>::iterator i = _units.begin(); i != _units.end(); ++i)
94 	{
95 		delete *i;
96 	}
97 
98 	for (std::vector<BattleItem*>::iterator i = _items.begin(); i != _items.end(); ++i)
99 	{
100 		delete *i;
101 	}
102 	for (std::vector<BattleItem*>::iterator i = _deleted.begin(); i != _deleted.end(); ++i)
103 	{
104 		delete *i;
105 	}
106 
107 	delete _pathfinding;
108 	delete _tileEngine;
109 }
110 
111 /**
112  * Loads the saved battle game from a YAML file.
113  * @param node YAML node.
114  * @param rule for the saved game.
115  * @param savedGame Pointer to saved game.
116  */
load(const YAML::Node & node,Ruleset * rule,SavedGame * savedGame)117 void SavedBattleGame::load(const YAML::Node &node, Ruleset *rule, SavedGame* savedGame)
118 {
119 	_mapsize_x = node["width"].as<int>(_mapsize_x);
120 	_mapsize_y = node["length"].as<int>(_mapsize_y);
121 	_mapsize_z = node["height"].as<int>(_mapsize_z);
122 	_missionType = node["missionType"].as<std::string>(_missionType);
123 	_globalShade = node["globalshade"].as<int>(_globalShade);
124 	_turn = node["turn"].as<int>(_turn);
125 	int selectedUnit = node["selectedUnit"].as<int>();
126 
127 	for (YAML::const_iterator i = node["mapdatasets"].begin(); i != node["mapdatasets"].end(); ++i)
128 	{
129 		std::string name = i->as<std::string>();
130 		MapDataSet *mds = rule->getMapDataSet(name);
131 		_mapDataSets.push_back(mds);
132 	}
133 
134 	initMap(_mapsize_x, _mapsize_y, _mapsize_z);
135 
136 	if (!node["tileTotalBytesPer"])
137 	{
138 		// binary tile data not found, load old-style text tiles :(
139 		for (YAML::const_iterator i = node["tiles"].begin(); i != node["tiles"].end(); ++i)
140 		{
141 			Position pos = (*i)["position"].as<Position>();
142 			getTile(pos)->load((*i));
143 		}
144 	}
145 	else
146 	{
147 		// load key to how the tile data was saved
148 		Tile::SerializationKey serKey;
149 		size_t totalTiles = node["totalTiles"].as<size_t>();
150 
151         memset(&serKey, 0, sizeof(Tile::SerializationKey));
152 		serKey.index = node["tileIndexSize"].as<Uint8>(serKey.index);
153 		serKey.totalBytes = node["tileTotalBytesPer"].as<Uint32>(serKey.totalBytes);
154 		serKey._fire = node["tileFireSize"].as<Uint8>(serKey._fire);
155 		serKey._smoke = node["tileSmokeSize"].as<Uint8>(serKey._smoke);
156 		serKey._mapDataID = node["tileIDSize"].as<Uint8>(serKey._mapDataID);
157 		serKey._mapDataSetID = node["tileSetIDSize"].as<Uint8>(serKey._mapDataSetID);
158 		serKey.boolFields = node["tileBoolFieldsSize"].as<Uint8>(1); // boolean flags used to be stored in an unmentioned byte (Uint8) :|
159 
160 		// load binary tile data!
161 		YAML::Binary binTiles = node["binTiles"].as<YAML::Binary>();
162 
163 		Uint8 *r = (Uint8*)binTiles.data();
164 		Uint8 *dataEnd = r + totalTiles * serKey.totalBytes;
165 
166 		while (r < dataEnd)
167 		{
168 			int index = unserializeInt(&r, serKey.index);
169 			assert (index >= 0 && index < _mapsize_x * _mapsize_z * _mapsize_y);
170 			_tiles[index]->loadBinary(r, serKey); // loadBinary's privileges to advance *r have been revoked
171 			r += serKey.totalBytes-serKey.index; // r is now incremented strictly by totalBytes in case there are obsolete fields present in the data
172 		}
173 	}
174 	if (_missionType == "STR_BASE_DEFENSE")
175 	{
176 		if (node["moduleMap"])
177 		{
178 			_baseModules = node["moduleMap"].as<std::vector< std::vector<std::pair<int, int> > > >();
179 		}
180 		else
181 		{
182 			// backwards compatibility: imperfect solution, modules that were completely destroyed
183 			// prior to saving and updating builds will be counted as indestructible.
184 			calculateModuleMap();
185 		}
186 	}
187 	for (YAML::const_iterator i = node["nodes"].begin(); i != node["nodes"].end(); ++i)
188 	{
189 		Node *n = new Node();
190 		n->load(*i);
191 		_nodes.push_back(n);
192 	}
193 
194 	for (YAML::const_iterator i = node["units"].begin(); i != node["units"].end(); ++i)
195 	{
196 		UnitFaction faction = (UnitFaction)(*i)["faction"].as<int>();
197 		int id = (*i)["soldierId"].as<int>();
198 		BattleUnit *unit;
199 		if (id < BattleUnit::MAX_SOLDIER_ID) // Unit is linked to a geoscape soldier
200 		{
201 			// look up the matching soldier
202 			unit = new BattleUnit(savedGame->getSoldier(id), faction);
203 		}
204 		else
205 		{
206 			std::string type = (*i)["genUnitType"].as<std::string>();
207 			std::string armor = (*i)["genUnitArmor"].as<std::string>();
208 			// create a new Unit.
209 			unit = new BattleUnit(rule->getUnit(type), faction, id, rule->getArmor(armor), savedGame->getDifficulty());
210 		}
211 		unit->load(*i);
212 		_units.push_back(unit);
213 		if (faction == FACTION_PLAYER)
214 		{
215 			if ((unit->getId() == selectedUnit) || (_selectedUnit == 0 && !unit->isOut()))
216 				_selectedUnit = unit;
217 
218 			// silly hack to fix mind controlled aliens
219 			// TODO: save stats instead? maybe some kind of weapon will affect them at some point.
220 			if (unit->getOriginalFaction() == FACTION_HOSTILE)
221 			{
222 				unit->adjustStats(savedGame->getDifficulty());
223 			}
224 		}
225 		if (unit->getStatus() != STATUS_DEAD)
226 		{
227 			if (const YAML::Node &ai = (*i)["AI"])
228 			{
229 				BattleAIState *aiState;
230 				if (faction == FACTION_NEUTRAL)
231 				{
232 					aiState = new CivilianBAIState(this, unit, 0);
233 				}
234 				else if (faction == FACTION_HOSTILE)
235 				{
236 					aiState = new AlienBAIState(this, unit, 0);
237 				}
238 				else
239 				{
240 					continue;
241 				}
242 				aiState->load(ai);
243 				unit->setAIState(aiState);
244 			}
245 		}
246 	}
247 	// matches up tiles and units
248 	resetUnitTiles();
249 
250 	for (YAML::const_iterator i = node["items"].begin(); i != node["items"].end(); ++i)
251 	{
252 		std::string type = (*i)["type"].as<std::string>();
253 		_itemId = (*i)["id"].as<int>(_itemId);
254 		if (rule->getItem(type))
255 		{
256 			BattleItem *item = new BattleItem(rule->getItem(type), &_itemId);
257 			item->load(*i);
258 			type = (*i)["inventoryslot"].as<std::string>();
259 			if (type != "NULL")
260 				item->setSlot(rule->getInventory(type));
261 			int owner = (*i)["owner"].as<int>();
262 			int unit = (*i)["unit"].as<int>();
263 
264 			// match up items and units
265 			for (std::vector<BattleUnit*>::iterator bu = _units.begin(); bu != _units.end(); ++bu)
266 			{
267 				if ((*bu)->getId() == owner)
268 				{
269 					item->moveToOwner(*bu);
270 				}
271 				if ((*bu)->getId() == unit)
272 				{
273 					item->setUnit(*bu);
274 				}
275 			}
276 
277 			// match up items and tiles
278 			if (item->getSlot() && item->getSlot()->getType() == INV_GROUND)
279 			{
280 				Position pos = (*i)["position"].as<Position>();
281 				if (pos.x != -1)
282 					getTile(pos)->addItem(item, rule->getInventory("STR_GROUND"));
283 			}
284 			_items.push_back(item);
285 		}
286 	}
287 
288 	// tie ammo items to their weapons, running through the items again
289 	std::vector<BattleItem*>::iterator weaponi = _items.begin();
290 	for (YAML::const_iterator i = node["items"].begin(); i != node["items"].end(); ++i)
291 	{
292 		if (rule->getItem((*i)["type"].as<std::string>()))
293 		{
294 			int ammo = (*i)["ammoItem"].as<int>();
295 			if (ammo != -1)
296 			{
297 				for (std::vector<BattleItem*>::iterator ammoi = _items.begin(); ammoi != _items.end(); ++ammoi)
298 				{
299 					if ((*ammoi)->getId() == ammo)
300 					{
301 						(*weaponi)->setAmmoItem((*ammoi));
302 						break;
303 					}
304 				}
305 			}
306 			 ++weaponi;
307 		}
308 	}
309 	_objectiveDestroyed = node["objectiveDestroyed"].as<bool>(_objectiveDestroyed);
310 	_tuReserved = (BattleActionType)node["tuReserved"].as<int>(_tuReserved);
311 	_kneelReserved = node["kneelReserved"].as<bool>(_kneelReserved);
312 }
313 
314 /**
315  * Loads the resources required by the map in the battle save.
316  * @param game Pointer to the game.
317  */
loadMapResources(Game * game)318 void SavedBattleGame::loadMapResources(Game *game)
319 {
320 	ResourcePack *res = game->getResourcePack();
321 	for (std::vector<MapDataSet*>::const_iterator i = _mapDataSets.begin(); i != _mapDataSets.end(); ++i)
322 	{
323 		(*i)->loadData();
324 		if (game->getRuleset()->getMCDPatch((*i)->getName()))
325 		{
326 			game->getRuleset()->getMCDPatch((*i)->getName())->modifyData(*i);
327 		}
328 	}
329 
330 	int mdsID, mdID;
331 
332 	for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
333 	{
334 		for (int part = 0; part < 4; part++)
335 		{
336 			_tiles[i]->getMapData(&mdID, &mdsID, part);
337 			if (mdID != -1 && mdsID != -1)
338 			{
339 				_tiles[i]->setMapData(_mapDataSets[mdsID]->getObjects()->at(mdID), mdID, mdsID, part);
340 			}
341 		}
342 	}
343 
344 	initUtilities(res);
345 	getTileEngine()->calculateSunShading();
346 	getTileEngine()->calculateTerrainLighting();
347 	getTileEngine()->calculateUnitLighting();
348 	getTileEngine()->recalculateFOV();
349 }
350 
351 /**
352  * Saves the saved battle game to a YAML file.
353  * @return YAML node.
354  */
save() const355 YAML::Node SavedBattleGame::save() const
356 {
357 	YAML::Node node;
358 	if (_objectiveDestroyed)
359 	{
360 		node["objectiveDestroyed"] = _objectiveDestroyed;
361 	}
362 	node["width"] = _mapsize_x;
363 	node["length"] = _mapsize_y;
364 	node["height"] = _mapsize_z;
365 	node["missionType"] = _missionType;
366 	node["globalshade"] = _globalShade;
367 	node["turn"] = _turn;
368 	node["selectedUnit"] = (_selectedUnit?_selectedUnit->getId():-1);
369 	for (std::vector<MapDataSet*>::const_iterator i = _mapDataSets.begin(); i != _mapDataSets.end(); ++i)
370 	{
371 		node["mapdatasets"].push_back((*i)->getName());
372 	}
373 #if 0
374 	for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
375 	{
376 		if (!_tiles[i]->isVoid())
377 		{
378 			node["tiles"].push_back(_tiles[i]->save());
379 		}
380 	}
381 #else
382 	// first, write out the field sizes we're going to use to write the tile data
383 	node["tileIndexSize"] = Tile::serializationKey.index;
384 	node["tileTotalBytesPer"] = Tile::serializationKey.totalBytes;
385 	node["tileFireSize"] = Tile::serializationKey._fire;
386 	node["tileSmokeSize"] = Tile::serializationKey._smoke;
387 	node["tileIDSize"] = Tile::serializationKey._mapDataID;
388 	node["tileSetIDSize"] = Tile::serializationKey._mapDataSetID;
389     node["tileBoolFieldsSize"] = Tile::serializationKey.boolFields;
390 
391 	size_t tileDataSize = Tile::serializationKey.totalBytes * _mapsize_z * _mapsize_y * _mapsize_x;
392 	Uint8* tileData = (Uint8*) calloc(tileDataSize, 1);
393 	Uint8* w = tileData;
394 
395 	for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
396 	{
397 		if (!_tiles[i]->isVoid())
398 		{
399 			serializeInt(&w, Tile::serializationKey.index, i);
400 			_tiles[i]->saveBinary(&w);
401 		}
402 		else
403 		{
404 			tileDataSize -= Tile::serializationKey.totalBytes;
405 		}
406 	}
407 	node["totalTiles"] = tileDataSize / Tile::serializationKey.totalBytes; // not strictly necessary, just convenient
408 	node["binTiles"] = YAML::Binary(tileData, tileDataSize);
409     free(tileData);
410 #endif
411 	for (std::vector<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i)
412 	{
413 		node["nodes"].push_back((*i)->save());
414 	}
415 	if (_missionType == "STR_BASE_DEFENSE")
416 	{
417 		node["moduleMap"] = _baseModules;
418 	}
419 	for (std::vector<BattleUnit*>::const_iterator i = _units.begin(); i != _units.end(); ++i)
420 	{
421 		node["units"].push_back((*i)->save());
422 	}
423 	for (std::vector<BattleItem*>::const_iterator i = _items.begin(); i != _items.end(); ++i)
424 	{
425 		node["items"].push_back((*i)->save());
426 	}
427 	node["tuReserved"] = (int)_tuReserved;
428     node["kneelReserved"] = _kneelReserved;
429 
430 	return node;
431 }
432 
433 /**
434  * Gets the array of tiles.
435  * @return A pointer to the Tile array.
436  */
getTiles() const437 Tile **SavedBattleGame::getTiles() const
438 {
439 	return _tiles;
440 }
441 
442 /**
443  * Initializes the array of tiles and creates a pathfinding object.
444  * @param mapsize_x
445  * @param mapsize_y
446  * @param mapsize_z
447  */
initMap(int mapsize_x,int mapsize_y,int mapsize_z)448 void SavedBattleGame::initMap(int mapsize_x, int mapsize_y, int mapsize_z)
449 {
450 	if (!_nodes.empty())
451 	{
452 		for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
453 		{
454 			delete _tiles[i];
455 		}
456 		delete[] _tiles;
457 
458 		for (std::vector<Node*>::iterator i = _nodes.begin(); i != _nodes.end(); ++i)
459 		{
460 			delete *i;
461 		}
462 
463 		_nodes.clear();
464 		_mapDataSets.clear();
465 	}
466 	_mapsize_x = mapsize_x;
467 	_mapsize_y = mapsize_y;
468 	_mapsize_z = mapsize_z;
469 	_tiles = new Tile*[_mapsize_z * _mapsize_y * _mapsize_x];
470 	/* create tile objects */
471 	for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
472 	{
473 		Position pos;
474 		getTileCoords(i, &pos.x, &pos.y, &pos.z);
475 		_tiles[i] = new Tile(pos);
476 	}
477 
478 }
479 
480 /**
481  * Initializes the map utilities.
482  * @param res Pointer to resource pack.
483  */
initUtilities(ResourcePack * res)484 void SavedBattleGame::initUtilities(ResourcePack *res)
485 {
486 	_pathfinding = new Pathfinding(this);
487 	_tileEngine = new TileEngine(this, res->getVoxelData());
488 }
489 
490 /**
491  * Sets the mission type.
492  * @param missionType The mission type.
493  */
setMissionType(const std::string & missionType)494 void SavedBattleGame::setMissionType(const std::string &missionType)
495 {
496 	_missionType = missionType;
497 }
498 
499 /**
500  * Gets the mission type.
501  * @return The mission type.
502  */
getMissionType() const503 std::string SavedBattleGame::getMissionType() const
504 {
505 	return _missionType;
506 }
507 
508 /**
509  * Sets the global shade.
510  * @param shade The global shade.
511  */
setGlobalShade(int shade)512 void SavedBattleGame::setGlobalShade(int shade)
513 {
514 	_globalShade = shade;
515 }
516 
517 /**
518  * Gets the global shade.
519  * @return The global shade.
520  */
getGlobalShade() const521 int SavedBattleGame::getGlobalShade() const
522 {
523 	return _globalShade;
524 }
525 
526 /**
527  * Gets the map width.
528  * @return The map width (Size X) in tiles.
529  */
getMapSizeX() const530 int SavedBattleGame::getMapSizeX() const
531 {
532 	return _mapsize_x;
533 }
534 
535 /**
536  * Gets the map length.
537  * @return The map length (Size Y) in tiles.
538  */
getMapSizeY() const539 int SavedBattleGame::getMapSizeY() const
540 {
541 	return _mapsize_y;
542 }
543 
544 /**
545  * Gets the map height.
546  * @return The map height (Size Z) in layers.
547  */
getMapSizeZ() const548 int SavedBattleGame::getMapSizeZ() const
549 {
550 	return _mapsize_z;
551 }
552 
553 /**
554  * Gets the map size in tiles.
555  * @return The map size.
556  */
getMapSizeXYZ() const557 int SavedBattleGame::getMapSizeXYZ() const
558 {
559 	return _mapsize_x * _mapsize_y * _mapsize_z;
560 }
561 
562 /**
563  * Converts a tile index to coordinates.
564  * @param index The (unique) tileindex.
565  * @param x Pointer to the X coordinate.
566  * @param y Pointer to the Y coordinate.
567  * @param z Pointer to the Z coordinate.
568  */
getTileCoords(int index,int * x,int * y,int * z) const569 void SavedBattleGame::getTileCoords(int index, int *x, int *y, int *z) const
570 {
571 	*z = index / (_mapsize_y * _mapsize_x);
572 	*y = (index % (_mapsize_y * _mapsize_x)) / _mapsize_x;
573 	*x = (index % (_mapsize_y * _mapsize_x)) % _mapsize_x;
574 }
575 
576 /**
577  * Gets the currently selected unit
578  * @return Pointer to BattleUnit.
579  */
getSelectedUnit() const580 BattleUnit *SavedBattleGame::getSelectedUnit() const
581 {
582 	return _selectedUnit;
583 }
584 
585 /**
586  * Sets the currently selected unit.
587  * @param unit Pointer to BattleUnit.
588  */
setSelectedUnit(BattleUnit * unit)589 void SavedBattleGame::setSelectedUnit(BattleUnit *unit)
590 {
591 	_selectedUnit = unit;
592 }
593 
594 /**
595 * Selects the previous player unit.
596 * @param checkReselect Whether to check if we should reselect a unit.
597 * @param setReselect Don't reselect a unit.
598 * @param checkInventory Whether to check if the unit has an inventory.
599 * @return Pointer to new selected BattleUnit, NULL if none can be selected.
600 * @sa selectPlayerUnit
601 */
selectPreviousPlayerUnit(bool checkReselect,bool setReselect,bool checkInventory)602 BattleUnit *SavedBattleGame::selectPreviousPlayerUnit(bool checkReselect, bool setReselect, bool checkInventory)
603 {
604 	return selectPlayerUnit(-1, checkReselect, setReselect, checkInventory);
605 }
606 
607 /**
608  * Selects the next player unit.
609  * @param checkReselect Whether to check if we should reselect a unit.
610  * @param setReselect Don't reselect a unit.
611  * @param checkInventory Whether to check if the unit has an inventory.
612  * @return Pointer to new selected BattleUnit, NULL if none can be selected.
613  * @sa selectPlayerUnit
614  */
selectNextPlayerUnit(bool checkReselect,bool setReselect,bool checkInventory)615 BattleUnit *SavedBattleGame::selectNextPlayerUnit(bool checkReselect, bool setReselect, bool checkInventory)
616 {
617 	return selectPlayerUnit(+1, checkReselect, setReselect, checkInventory);
618 }
619 
620 /**
621  * Selects the next player unit in a certain direction.
622  * @param dir Direction to select, eg. -1 for previous and 1 for next.
623  * @param checkReselect Whether to check if we should reselect a unit.
624  * @param setReselect Don't reselect a unit.
625  * @param checkInventory Whether to check if the unit has an inventory.
626  * @return Pointer to new selected BattleUnit, NULL if none can be selected.
627  */
selectPlayerUnit(int dir,bool checkReselect,bool setReselect,bool checkInventory)628 BattleUnit *SavedBattleGame::selectPlayerUnit(int dir, bool checkReselect, bool setReselect, bool checkInventory)
629 {
630 	if (_selectedUnit != 0 && setReselect)
631 	{
632 		_selectedUnit->dontReselect();
633 	}
634 	if (_units.empty())
635 	{
636 		return 0;
637 	}
638 
639 	std::vector<BattleUnit*>::iterator begin, end;
640 	if (dir > 0)
641 	{
642 		begin = _units.begin();
643 		end = _units.end()-1;
644 	}
645 	else if (dir < 0)
646 	{
647 		begin = _units.end()-1;
648 		end = _units.begin();
649 	}
650 
651 	std::vector<BattleUnit*>::iterator i = std::find(_units.begin(), _units.end(), _selectedUnit);
652 	do
653 	{
654 		// no unit selected
655 		if (i == _units.end())
656 		{
657 			i = begin;
658 			continue;
659 		}
660 		if (i != end)
661 		{
662 			i += dir;
663 		}
664 		// reached the end, wrap-around
665 		else
666 		{
667 			i = begin;
668 		}
669 		// back to where we started... no more units found
670 		if (*i == _selectedUnit)
671 		{
672 			if (checkReselect && !_selectedUnit->reselectAllowed())
673 				_selectedUnit = 0;
674 			return _selectedUnit;
675 		}
676 		else if (_selectedUnit == 0 && i == begin)
677 		{
678 			return _selectedUnit;
679 		}
680 	}
681 	while (!(*i)->isSelectable(_side, checkReselect, checkInventory));
682 
683 	_selectedUnit = (*i);
684 	return _selectedUnit;
685 }
686 
687 /**
688  * Selects the unit at the given position on the map.
689  * @param pos Position.
690  * @return Pointer to a BattleUnit, or 0 when none is found.
691  */
selectUnit(const Position & pos)692 BattleUnit *SavedBattleGame::selectUnit(const Position& pos)
693 {
694 	BattleUnit *bu = getTile(pos)->getUnit();
695 
696 	if (bu && bu->isOut())
697 	{
698 		return 0;
699 	}
700 	else
701 	{
702 		return bu;
703 	}
704 }
705 
706 /**
707  * Gets the list of nodes.
708  * @return Pointer to the list of nodes.
709  */
getNodes()710 std::vector<Node*> *SavedBattleGame::getNodes()
711 {
712 	return &_nodes;
713 }
714 
715 /**
716  * Gets the list of units.
717  * @return Pointer to the list of units.
718  */
getUnits()719 std::vector<BattleUnit*> *SavedBattleGame::getUnits()
720 {
721 	return &_units;
722 }
723 
724 /**
725  * Gets the list of items.
726  * @return Pointer to the list of items.
727  */
getItems()728 std::vector<BattleItem*> *SavedBattleGame::getItems()
729 {
730 	return &_items;
731 }
732 
733 /**
734  * Gets the pathfinding object.
735  * @return Pointer to the pathfinding object.
736  */
getPathfinding() const737 Pathfinding *SavedBattleGame::getPathfinding() const
738 {
739 	return _pathfinding;
740 }
741 
742 /**
743  * Gets the terrain modifier object.
744  * @return Pointer to the terrain modifier object.
745  */
getTileEngine() const746 TileEngine *SavedBattleGame::getTileEngine() const
747 {
748 	return _tileEngine;
749 }
750 
751 /**
752 * Gets the array of mapblocks.
753 * @return Pointer to the array of mapblocks.
754 */
getMapDataSets()755 std::vector<MapDataSet*> *SavedBattleGame::getMapDataSets()
756 {
757 	return &_mapDataSets;
758 }
759 
760 /**
761  * Gets the side currently playing.
762  * @return The unit faction currently playing.
763  */
getSide() const764 UnitFaction SavedBattleGame::getSide() const
765 {
766 	return _side;
767 }
768 
769 /**
770  * Gets the current turn number.
771  * @return The current turn.
772  */
getTurn() const773 int SavedBattleGame::getTurn() const
774 {
775 	return _turn;
776 }
777 
778 /**
779  * Ends the current turn and progresses to the next one.
780  */
endTurn()781 void SavedBattleGame::endTurn()
782 {
783 	if (_side == FACTION_PLAYER)
784 	{
785 		if (_selectedUnit && _selectedUnit->getOriginalFaction() == FACTION_PLAYER)
786 			_lastSelectedUnit = _selectedUnit;
787 		_selectedUnit =  0;
788 		_side = FACTION_HOSTILE;
789 	}
790 	else if (_side == FACTION_HOSTILE)
791 	{
792 		_side = FACTION_NEUTRAL;
793 		// if there is no neutral team, we skip this and instantly prepare the new turn for the player
794 		if (selectNextPlayerUnit() == 0)
795 		{
796 			prepareNewTurn();
797 			_turn++;
798 			_side = FACTION_PLAYER;
799 			if (_lastSelectedUnit && _lastSelectedUnit->isSelectable(FACTION_PLAYER, false, false))
800 				_selectedUnit = _lastSelectedUnit;
801 			else
802 				selectNextPlayerUnit();
803 			while (_selectedUnit && _selectedUnit->getFaction() != FACTION_PLAYER)
804 				selectNextPlayerUnit();
805 		}
806 
807 	}
808 	else if (_side == FACTION_NEUTRAL)
809 	{
810 		prepareNewTurn();
811 		_turn++;
812 		_side = FACTION_PLAYER;
813 		if (_lastSelectedUnit && _lastSelectedUnit->isSelectable(FACTION_PLAYER, false, false))
814 			_selectedUnit = _lastSelectedUnit;
815 		else
816 			selectNextPlayerUnit();
817 		while (_selectedUnit && _selectedUnit->getFaction() != FACTION_PLAYER)
818 			selectNextPlayerUnit();
819 	}
820 	int liveSoldiers, liveAliens;
821 
822 	_battleState->getBattleGame()->tallyUnits(liveAliens, liveSoldiers, false);
823 
824 	if (_turn >= 20 || liveAliens < 2)
825 	{
826 		_cheating = true;
827 	}
828 
829 	if (_side == FACTION_PLAYER)
830 	{
831 		// update the "number of turns since last spotted"
832 		for (std::vector<BattleUnit*>::iterator i = _units.begin(); i != _units.end(); ++i)
833 		{
834 			if ((*i)->getTurnsSinceSpotted() < 255)
835 			{
836 				(*i)->setTurnsSinceSpotted((*i)->getTurnsSinceSpotted() +	1);
837 			}
838 			if (_cheating && (*i)->getFaction() == FACTION_PLAYER && !(*i)->isOut())
839 			{
840 				(*i)->setTurnsSinceSpotted(0);
841 			}
842 		}
843 	}
844 	// hide all aliens (VOF calculations below will turn them visible again)
845 	for (std::vector<BattleUnit*>::iterator i = _units.begin(); i != _units.end(); ++i)
846 	{
847 		if ((*i)->getFaction() == _side)
848 		{
849 			(*i)->prepareNewTurn();
850 		}
851 		if ((*i)->getFaction() != FACTION_PLAYER)
852 		{
853 			(*i)->setVisible(false);
854 		}
855 	}
856 
857 	// re-run calculateFOV() *after* all aliens have been set not-visible
858 	_tileEngine->recalculateFOV();
859 
860 	if (_side != FACTION_PLAYER)
861 		selectNextPlayerUnit();
862 }
863 
864 /**
865  * Turns on debug mode.
866  */
setDebugMode()867 void SavedBattleGame::setDebugMode()
868 {
869 	for (int i = 0; i < _mapsize_z * _mapsize_y * _mapsize_x; ++i)
870 	{
871 		_tiles[i]->setDiscovered(true, 2);
872 	}
873 
874 	_debugMode = true;
875 }
876 
877 /**
878  * Gets the current debug mode.
879  * @return Debug mode.
880  */
getDebugMode() const881 bool SavedBattleGame::getDebugMode() const
882 {
883 	return _debugMode;
884 }
885 
886 /**
887  * Gets the BattlescapeState.
888  * @return Pointer to the BattlescapeState.
889  */
getBattleState()890 BattlescapeState *SavedBattleGame::getBattleState()
891 {
892 	return _battleState;
893 }
894 
895 /**
896  * Gets the BattlescapeState.
897  * @return Pointer to the BattlescapeState.
898  */
getBattleGame()899 BattlescapeGame *SavedBattleGame::getBattleGame()
900 {
901 	return _battleState->getBattleGame();
902 }
903 
904 /**
905  * Sets the BattlescapeState.
906  * @param bs A Pointer to a BattlescapeState.
907  */
setBattleState(BattlescapeState * bs)908 void SavedBattleGame::setBattleState(BattlescapeState *bs)
909 {
910 	_battleState = bs;
911 }
912 
913 /**
914  * Resets all the units to their current standing tile(s).
915  */
resetUnitTiles()916 void SavedBattleGame::resetUnitTiles()
917 {
918 	for (std::vector<BattleUnit*>::iterator i = _units.begin(); i != _units.end(); ++i)
919 	{
920 		if (!(*i)->isOut())
921 		{
922 			int size = (*i)->getArmor()->getSize() - 1;
923 			if ((*i)->getTile() && (*i)->getTile()->getUnit() == (*i))
924 			{
925 				for (int x = size; x >= 0; x--)
926 				{
927 					for (int y = size; y >= 0; y--)
928 					{
929 						getTile((*i)->getTile()->getPosition() + Position(x,y,0))->setUnit(0);
930 					}
931 				}
932 			}
933 			for (int x = size; x >= 0; x--)
934 			{
935 				for (int y = size; y >= 0; y--)
936 				{
937 					Tile *t = getTile((*i)->getPosition() + Position(x,y,0));
938 					t->setUnit((*i), getTile(t->getPosition() + Position(0,0,-1)));
939 				}
940 			}
941 
942 		}
943 		if ((*i)->getFaction() == FACTION_PLAYER)
944 		{
945 			(*i)->setVisible(true);
946 		}
947 	}
948 }
949 
950 /**
951  * Gives access to the "storage space" vector, for distribution of items in base defense missions.
952  * @return Vector of storage positions.
953  */
getStorageSpace()954 std::vector<Position> &SavedBattleGame::getStorageSpace()
955 {
956 	return _storageSpace;
957 }
958 
959 /**
960  * Move all the leftover items in base defense missions to random locations in the storage facilities
961  * @param t the tile where all our goodies are initially stored.
962  */
randomizeItemLocations(Tile * t)963 void SavedBattleGame::randomizeItemLocations(Tile *t)
964 {
965 	if (!_storageSpace.empty())
966 	{
967 		for (std::vector<BattleItem*>::iterator it = t->getInventory()->begin(); it != t->getInventory()->end();)
968 		{
969 			if ((*it)->getSlot()->getId() == "STR_GROUND")
970 			{
971 				getTile(_storageSpace.at(RNG::generate(0, _storageSpace.size() -1)))->addItem(*it, (*it)->getSlot());
972 				it = t->getInventory()->erase(it);
973 			}
974 			else
975 			{
976 				++it;
977 			}
978 		}
979 	}
980 }
981 /**
982  * Removes an item from the game. Eg. when ammo item is depleted.
983  * @param item The Item to remove.
984  */
removeItem(BattleItem * item)985 void SavedBattleGame::removeItem(BattleItem *item)
986 {
987 	// due to strange design, the item has to be removed from the tile it is on too (if it is on a tile)
988 	Tile *t = item->getTile();
989 	BattleUnit *b = item->getOwner();
990 	if (t)
991 	{
992 		for (std::vector<BattleItem*>::iterator it = t->getInventory()->begin(); it != t->getInventory()->end(); ++it)
993 		{
994 			if ((*it) == item)
995 			{
996 				t->getInventory()->erase(it);
997 				break;
998 			}
999 		}
1000 	}
1001 	if (b)
1002 	{
1003 		for (std::vector<BattleItem*>::iterator it = b->getInventory()->begin(); it != b->getInventory()->end(); ++it)
1004 		{
1005 			if ((*it) == item)
1006 			{
1007 				b->getInventory()->erase(it);
1008 				break;
1009 			}
1010 		}
1011 	}
1012 
1013 	for (std::vector<BattleItem*>::iterator i = _items.begin(); i != _items.end(); ++i)
1014 	{
1015 		if (*i == item)
1016 		{
1017 			_items.erase(i);
1018 			break;
1019 		}
1020 	}
1021 
1022 	_deleted.push_back(item);
1023 	/*
1024 	for (int i = 0; i < _mapsize_x * _mapsize_y * _mapsize_z; ++i)
1025 	{
1026 		for (std::vector<BattleItem*>::iterator it = _tiles[i]->getInventory()->begin(); it != _tiles[i]->getInventory()->end(); )
1027 		{
1028 			if ((*it) == item)
1029 			{
1030 				it = _tiles[i]->getInventory()->erase(it);
1031 				return;
1032 			}
1033 			++it;
1034 		}
1035 	}
1036 	*/
1037 
1038 }
1039 
1040 /**
1041  * Sets whether the mission was aborted or successful.
1042  * @param flag True, if the mission was aborted, or false, if the mission was successful.
1043  */
setAborted(bool flag)1044 void SavedBattleGame::setAborted(bool flag)
1045 {
1046 	_aborted = flag;
1047 }
1048 
1049 /**
1050  * Returns whether the mission was aborted or successful.
1051  * @return True, if the mission was aborted, or false, if the mission was successful.
1052  */
isAborted() const1053 bool SavedBattleGame::isAborted() const
1054 {
1055 	return _aborted;
1056 }
1057 
1058 /**
1059  * Sets whether the objective is destroyed.
1060  * @param flag True if the objective is destroyed.
1061  */
setObjectiveDestroyed(bool flag)1062 void SavedBattleGame::setObjectiveDestroyed(bool flag)
1063 {
1064 	_objectiveDestroyed = flag;
1065 	if (flag && Options::battleAutoEnd)
1066 	{
1067 		setSelectedUnit(0);
1068 		_battleState->getBattleGame()->cancelCurrentAction(true);
1069 		_battleState->getBattleGame()->requestEndTurn();
1070 	}
1071 }
1072 
1073 /**
1074  * Returns whether the objective is detroyed.
1075  * @return True if the objective is destroyed.
1076  */
isObjectiveDestroyed()1077 bool SavedBattleGame::isObjectiveDestroyed()
1078 {
1079 	return _objectiveDestroyed;
1080 }
1081 
1082 /**
1083  * Gets the current item ID.
1084  * @return Current item ID pointer.
1085  */
getCurrentItemId()1086 int *SavedBattleGame::getCurrentItemId()
1087 {
1088 	return &_itemId;
1089 }
1090 
1091 /**
1092  * Finds a fitting node where a unit can spawn.
1093  * @param nodeRank Rank of the node (this is not the rank of the alien!).
1094  * @param unit Pointer to the unit (to get its position).
1095  * @return Pointer to the chosen node.
1096  */
getSpawnNode(int nodeRank,BattleUnit * unit)1097 Node *SavedBattleGame::getSpawnNode(int nodeRank, BattleUnit *unit)
1098 {
1099 	int highestPriority = -1;
1100 	std::vector<Node*> compliantNodes;
1101 
1102 	for (std::vector<Node*>::iterator i = getNodes()->begin(); i != getNodes()->end(); ++i)
1103 	{
1104 		if ((*i)->getRank() == nodeRank										// ranks must match
1105 			&& (!((*i)->getType() & Node::TYPE_SMALL)
1106 				|| unit->getArmor()->getSize() == 1)				// the small unit bit is not set or the unit is small
1107 			&& (!((*i)->getType() & Node::TYPE_FLYING)
1108 				|| unit->getArmor()->getMovementType() == MT_FLY)// the flying unit bit is not set or the unit can fly
1109 			&& (*i)->getPriority() > 0										// priority 0 is no spawnplace
1110 			&& setUnitPosition(unit, (*i)->getPosition(), true))		// check if not already occupied
1111 		{
1112 			if ((*i)->getPriority() > highestPriority)
1113 			{
1114 				highestPriority = (*i)->getPriority();
1115 				compliantNodes.clear(); // drop the last nodes, as we found a higher priority now
1116 			}
1117 			if ((*i)->getPriority() == highestPriority)
1118 			{
1119 				compliantNodes.push_back((*i));
1120 			}
1121 		}
1122 	}
1123 
1124 	if (compliantNodes.empty()) return 0;
1125 
1126 	int n = RNG::generate(0, compliantNodes.size() - 1);
1127 
1128 	return compliantNodes[n];
1129 }
1130 
1131 /**
1132  * Finds a fitting node where a unit can patrol to.
1133  * @param scout Is the unit scouting?
1134  * @param unit Pointer to the unit (to get its position).
1135  * @param fromNode Pointer to the node the unit is at.
1136  * @return Pointer to the choosen node.
1137  */
getPatrolNode(bool scout,BattleUnit * unit,Node * fromNode)1138 Node *SavedBattleGame::getPatrolNode(bool scout, BattleUnit *unit, Node *fromNode)
1139 {
1140 	std::vector<Node *> compliantNodes;
1141 	Node *preferred = 0;
1142 
1143 	if (fromNode == 0)
1144 	{
1145 		if (Options::traceAI) { Log(LOG_INFO) << "This alien got lost. :("; }
1146 		fromNode = getNodes()->at(RNG::generate(0, getNodes()->size() - 1));
1147 	}
1148 
1149 	// scouts roam all over while all others shuffle around to adjacent nodes at most:
1150 	const int end = scout ? getNodes()->size() : fromNode->getNodeLinks()->size();
1151 
1152 	for (int i = 0; i < end; ++i)
1153 	{
1154 		if (!scout && fromNode->getNodeLinks()->at(i) < 1) continue;
1155 
1156 		Node *n = getNodes()->at(scout ? i : fromNode->getNodeLinks()->at(i));
1157 		if ((n->getFlags() > 0 || n->getRank() > 0 || scout)											// for non-scouts we find a node with a desirability above 0
1158 			&& (!(n->getType() & Node::TYPE_SMALL) || unit->getArmor()->getSize() == 1)					// the small unit bit is not set or the unit is small
1159 			&& (!(n->getType() & Node::TYPE_FLYING) || unit->getArmor()->getMovementType() == MT_FLY)	// the flying unit bit is not set or the unit can fly
1160 			&& !n->isAllocated()																		// check if not allocated
1161 			&& !(n->getType() & Node::TYPE_DANGEROUS)													// don't go there if an alien got shot there; stupid behavior like that
1162 			&& setUnitPosition(unit, n->getPosition(), true)											// check if not already occupied
1163 			&& getTile(n->getPosition()) && !getTile(n->getPosition())->getFire()						// you are not a firefighter; do not patrol into fire
1164 			&& (unit->getFaction() != FACTION_HOSTILE || !getTile(n->getPosition())->getDangerous())	// aliens don't run into a grenade blast
1165 			&& (!scout || n != fromNode)																// scouts push forward
1166 			&& n->getPosition().x > 0 && n->getPosition().y > 0)
1167 		{
1168 			if (!preferred
1169 				|| (preferred->getRank() == Node::nodeRank[unit->getRankInt()][0] && preferred->getFlags() < n->getFlags())
1170 				|| preferred->getFlags() < n->getFlags())
1171 			{
1172 				preferred = n;
1173 			}
1174 			compliantNodes.push_back(n);
1175 		}
1176 	}
1177 
1178 	if (compliantNodes.empty())
1179 	{
1180 		if (Options::traceAI) { Log(LOG_INFO) << (scout ? "Scout " : "Guard") << " found on patrol node! XXX XXX XXX"; }
1181 		if (unit->getArmor()->getSize() > 1 && !scout)
1182 		{
1183 			return getPatrolNode(true, unit, fromNode); // move dammit
1184 		}
1185 		else
1186 			return 0;
1187 	}
1188 
1189 	if (scout)
1190 	{
1191 		// scout picks a random destination:
1192 		return compliantNodes[RNG::generate(0, compliantNodes.size() - 1)];
1193 	}
1194 	else
1195 	{
1196 		if (!preferred) return 0;
1197 
1198 		// non-scout patrols to highest value unoccupied node that's not fromNode
1199 		if (Options::traceAI) { Log(LOG_INFO) << "Choosing node flagged " << preferred->getFlags(); }
1200 		return preferred;
1201 	}
1202 }
1203 
1204 /**
1205  * Carries out new turn preparations such as fire and smoke spreading.
1206  */
prepareNewTurn()1207 void SavedBattleGame::prepareNewTurn()
1208 {
1209 	std::vector<Tile*> tilesOnFire;
1210 	std::vector<Tile*> tilesOnSmoke;
1211 
1212 	// prepare a list of tiles on fire
1213 	for (int i = 0; i < _mapsize_x * _mapsize_y * _mapsize_z; ++i)
1214 	{
1215 		if (getTiles()[i]->getFire() > 0)
1216 		{
1217 			tilesOnFire.push_back(getTiles()[i]);
1218 		}
1219 	}
1220 
1221 	// first: fires spread
1222 	for (std::vector<Tile*>::iterator i = tilesOnFire.begin(); i != tilesOnFire.end(); ++i)
1223 	{
1224 		// if we haven't added fire here this turn
1225 		if ((*i)->getOverlaps() == 0)
1226 		{
1227 			// reduce the fire timer
1228 			(*i)->setFire((*i)->getFire() -1);
1229 
1230 			// if we're still burning
1231 			if ((*i)->getFire())
1232 			{
1233 				// propegate in four cardinal directions (0, 2, 4, 6)
1234 				for (int dir = 0; dir <= 6; dir += 2)
1235 				{
1236 					Position pos;
1237 					Pathfinding::directionToVector(dir, &pos);
1238 					Tile *t = getTile((*i)->getPosition() + pos);
1239 					// if there's no wall blocking the path of the flames...
1240 					if (t && getTileEngine()->horizontalBlockage((*i), t, DT_IN) == 0)
1241 					{
1242 						// attempt to set this tile on fire
1243 						t->ignite((*i)->getSmoke());
1244 					}
1245 				}
1246 			}
1247 			// fire has burnt out
1248 			else
1249 			{
1250 				(*i)->setSmoke(0);
1251 				// burn this tile, and any object in it, if it's not fireproof/indestructible.
1252 				if ((*i)->getMapData(MapData::O_OBJECT))
1253 				{
1254 					if ((*i)->getMapData(MapData::O_OBJECT)->getFlammable() != 255 && (*i)->getMapData(MapData::O_OBJECT)->getArmor() != 255)
1255 					{
1256 						if ((*i)->destroy(MapData::O_OBJECT))
1257 						{
1258 							_objectiveDestroyed = true;
1259 						}
1260 						if ((*i)->destroy(MapData::O_FLOOR))
1261 						{
1262 							_objectiveDestroyed = true;
1263 						}
1264 					}
1265 				}
1266 				else if ((*i)->getMapData(MapData::O_FLOOR))
1267 				{
1268 					if ((*i)->getMapData(MapData::O_FLOOR)->getFlammable() != 255 && (*i)->getMapData(MapData::O_FLOOR)->getArmor() != 255)
1269 					{
1270 						if ((*i)->destroy(MapData::O_FLOOR))
1271 						{
1272 							_objectiveDestroyed = true;
1273 						}
1274 					}
1275 				}
1276 			}
1277 		}
1278 	}
1279 
1280 	// prepare a list of tiles on fire/with smoke in them (smoke acts as fire intensity)
1281 	for (int i = 0; i < _mapsize_x * _mapsize_y * _mapsize_z; ++i)
1282 	{
1283 		if (getTiles()[i]->getSmoke() > 0)
1284 		{
1285 			tilesOnSmoke.push_back(getTiles()[i]);
1286 		}
1287 	}
1288 
1289 	// now make the smoke spread.
1290 	for (std::vector<Tile*>::iterator i = tilesOnSmoke.begin(); i != tilesOnSmoke.end(); ++i)
1291 	{
1292 		// smoke and fire follow slightly different rules.
1293 		if ((*i)->getFire() == 0)
1294 		{
1295 			// reduce the smoke counter
1296 			(*i)->setSmoke((*i)->getSmoke() - 1);
1297 			// if we're still smoking
1298 			if ((*i)->getSmoke())
1299 			{
1300 				// spread in four cardinal directions
1301 				for (int dir = 0; dir <= 6; dir += 2)
1302 				{
1303 					Position pos;
1304 					Pathfinding::directionToVector(dir, &pos);
1305 					Tile *t = getTile((*i)->getPosition() + pos);
1306 					// as long as there are no walls blocking us
1307 					if (t && getTileEngine()->horizontalBlockage((*i), t, DT_SMOKE) == 0)
1308 					{
1309 						// only add smoke to empty tiles, or tiles with no fire, and smoke that was added this turn
1310 						if (t->getSmoke() == 0 || (t->getFire() == 0 && t->getOverlaps() != 0))
1311 						{
1312 							t->addSmoke((*i)->getSmoke());
1313 						}
1314 					}
1315 				}
1316 			}
1317 		}
1318 		else
1319 		{
1320 			// smoke from fire spreads upwards one level if there's no floor blocking it.
1321 			Position pos = Position(0,0,1);
1322 			Tile *t = getTile((*i)->getPosition() + pos);
1323 			if (t && t->hasNoFloor(*i))
1324 			{
1325 				// only add smoke equal to half the intensity of the fire
1326 				t->addSmoke((*i)->getSmoke()/2);
1327 			}
1328 			// then it spreads in the four cardinal directions.
1329 			for (int dir = 0; dir <= 6; dir += 2)
1330 			{
1331 				Pathfinding::directionToVector(dir, &pos);
1332 				t = getTile((*i)->getPosition() + pos);
1333 				if (t && getTileEngine()->horizontalBlockage((*i), t, DT_SMOKE) == 0)
1334 				{
1335 					t->addSmoke((*i)->getSmoke()/2);
1336 				}
1337 			}
1338 		}
1339 	}
1340 
1341 	if (!tilesOnFire.empty() || !tilesOnSmoke.empty())
1342 	{
1343 		// do damage to units, average out the smoke, etc.
1344 		for (int i = 0; i < _mapsize_x * _mapsize_y * _mapsize_z; ++i)
1345 		{
1346 			if (getTiles()[i]->getSmoke() != 0)
1347 				getTiles()[i]->prepareNewTurn();
1348 		}
1349 		// fires could have been started, stopped or smoke could reveal/conceal units.
1350 		getTileEngine()->calculateTerrainLighting();
1351 	}
1352 
1353 	reviveUnconsciousUnits();
1354 }
1355 
1356 /**
1357  * Checks for units that are unconcious and revives them if they shouldn't be.
1358  *
1359  * Revived units need a tile to stand on. If the unit's current position is occupied, then
1360  * all directions around the tile are searched for a free tile to place the unit in.
1361  * If no free tile is found the unit stays unconscious.
1362  */
reviveUnconsciousUnits()1363 void SavedBattleGame::reviveUnconsciousUnits()
1364 {
1365 	for (std::vector<BattleUnit*>::iterator i = getUnits()->begin(); i != getUnits()->end(); ++i)
1366 	{
1367 		if ((*i)->getArmor()->getSize() == 1)
1368 		{
1369 			Position originalPosition = (*i)->getPosition();
1370 			if (originalPosition == Position(-1, -1, -1))
1371 			{
1372 				for (std::vector<BattleItem*>::iterator j = _items.begin(); j != _items.end(); ++j)
1373 				{
1374 					if ((*j)->getUnit() && (*j)->getUnit() == *i && (*j)->getOwner())
1375 					{
1376 						originalPosition = (*j)->getOwner()->getPosition();
1377 					}
1378 				}
1379 			}
1380 			if((*i)->getStatus() == STATUS_UNCONSCIOUS && (*i)->getStunlevel() < (*i)->getHealth() && (*i)->getHealth() > 0)
1381 			{
1382 				if (placeUnitNearPosition((*i), originalPosition))
1383 				{
1384 					// recover from unconscious
1385 					(*i)->turn(false); // makes the unit stand up again
1386 					(*i)->kneel(false);
1387 					(*i)->setCache(0);
1388 					getTileEngine()->calculateFOV((*i));
1389 					getTileEngine()->calculateUnitLighting();
1390 					removeUnconsciousBodyItem((*i));
1391 					break;
1392 				}
1393 			}
1394 		}
1395 	}
1396 }
1397 
1398 /**
1399  * Removes the body item that corresponds to the unit.
1400  */
removeUnconsciousBodyItem(BattleUnit * bu)1401 void SavedBattleGame::removeUnconsciousBodyItem(BattleUnit *bu)
1402 {
1403 	// remove the unconscious body item corresponding to this unit
1404 	for (std::vector<BattleItem*>::iterator it = getItems()->begin(); it != getItems()->end(); )
1405 	{
1406 		if ((*it)->getUnit() == bu)
1407 		{
1408 			removeItem((*it));
1409 			break;
1410 		}
1411 		++it;
1412 	}
1413 }
1414 
1415 /**
1416  * Places units on the map. Handles large units that are placed on multiple tiles.
1417  * @param bu The unit to be placed.
1418  * @param position The position to place the unit.
1419  * @param testOnly If true then just checks if the unit can be placed at the position.
1420  * @return True if the unit could be successfully placed.
1421  */
setUnitPosition(BattleUnit * bu,const Position & position,bool testOnly)1422 bool SavedBattleGame::setUnitPosition(BattleUnit *bu, const Position &position, bool testOnly)
1423 {
1424 	int size = bu->getArmor()->getSize() - 1;
1425 
1426 	// first check if the tiles are occupied
1427 	for (int x = size; x >= 0; x--)
1428 	{
1429 		for (int y = size; y >= 0; y--)
1430 		{
1431 			Tile *t = getTile(position + Position(x,y,0));
1432 			Tile *tb = getTile(position + Position(x,y,-1));
1433 			if (t == 0 || (t->getUnit() != 0 && t->getUnit() != bu) || t->getTUCost(MapData::O_OBJECT, bu->getArmor()->getMovementType()) == 255 || (t->hasNoFloor(tb) && bu->getArmor()->getMovementType() != MT_FLY))
1434 			{
1435 				return false;
1436 			}
1437 		}
1438 	}
1439 
1440 	if (size > 0)
1441 	{
1442 		getPathfinding()->setUnit(bu);
1443 		for (int dir = 2; dir <= 4; ++dir)
1444 		{
1445 			if (getPathfinding()->isBlocked(getTile(position), 0, dir, 0))
1446 				return false;
1447 		}
1448 	}
1449 
1450 	if (testOnly) return true;
1451 
1452 	for (int x = size; x >= 0; x--)
1453 	{
1454 		for (int y = size; y >= 0; y--)
1455 		{
1456 			if (x==0 && y==0)
1457 			{
1458 				bu->setPosition(position);
1459 			}
1460 			getTile(position + Position(x,y,0))->setUnit(bu, getTile(position + Position(x,y,-1)));
1461 		}
1462 	}
1463 
1464 	return true;
1465 }
1466 
1467 /**
1468  * @brief Checks whether anyone on a particular faction is looking at the unit.
1469  *
1470  * Similar to getSpottingUnits() but returns a bool and stops searching if one positive hit is found.
1471  *
1472  * @param faction Faction to check through.
1473  * @param unit Whom to spot.
1474  * @return True when the unit can be seen
1475  */
eyesOnTarget(UnitFaction faction,BattleUnit * unit)1476 bool SavedBattleGame::eyesOnTarget(UnitFaction faction, BattleUnit* unit)
1477 {
1478 	for (std::vector<BattleUnit*>::iterator i = getUnits()->begin(); i != getUnits()->end(); ++i)
1479 	{
1480 		if ((*i)->getFaction() != faction) continue;
1481 
1482 		std::vector<BattleUnit*> *vis = (*i)->getVisibleUnits();
1483 		if (std::find(vis->begin(), vis->end(), unit) != vis->end()) return true;
1484 		// aliens know the location of all XCom agents sighted by all other aliens due to sharing locations over their space-walkie-talkies
1485 	}
1486 
1487 	return false;
1488 }
1489 
1490 /**
1491  * Adds this unit to the vector of falling units,
1492  * if it doesn't already exist.
1493  * @param unit The unit.
1494  * @return Was the unit added?
1495  */
addFallingUnit(BattleUnit * unit)1496 bool SavedBattleGame::addFallingUnit(BattleUnit* unit)
1497 {
1498 	bool add = true;
1499 	for (std::list<BattleUnit*>::iterator i = _fallingUnits.begin(); i != _fallingUnits.end(); ++i)
1500 	{
1501 		if (unit == *i)
1502 		{
1503 			add = false;
1504 			break;
1505 		}
1506 	}
1507 	if (add)
1508 	{
1509 		_fallingUnits.push_front(unit);
1510 		_unitsFalling = true;
1511 	}
1512 	return add;
1513 }
1514 
1515 /**
1516  * Gets all units in the battlescape that are falling.
1517  * @return The falling units in the battlescape.
1518  */
getFallingUnits()1519 std::list<BattleUnit*> *SavedBattleGame::getFallingUnits()
1520 {
1521 	return &_fallingUnits;
1522 }
1523 
1524 /**
1525  * Toggles the switch that says "there are units falling, start the fall state".
1526  * @param fall True if there are any units falling in the battlescape.
1527  */
setUnitsFalling(bool fall)1528 void SavedBattleGame::setUnitsFalling(bool fall)
1529 {
1530 	_unitsFalling = fall;
1531 }
1532 
1533 /**
1534  * Returns whether there are any units falling in the battlescape.
1535  * @return True if there are any units falling in the battlescape.
1536  */
getUnitsFalling() const1537 bool SavedBattleGame::getUnitsFalling() const
1538 {
1539 	return _unitsFalling;
1540 }
1541 
1542 /**
1543  * Gets the highest ranked, living XCom unit.
1544  * @return The highest ranked, living XCom unit.
1545  */
getHighestRankedXCom()1546 BattleUnit* SavedBattleGame::getHighestRankedXCom()
1547 {
1548 	BattleUnit* highest = 0;
1549 	for (std::vector<BattleUnit*>::iterator j = _units.begin(); j != _units.end(); ++j)
1550 	{
1551 		if ((*j)->getOriginalFaction() == FACTION_PLAYER && !(*j)->isOut())
1552 		{
1553 			if (highest == 0 || (*j)->getRankInt() > highest->getRankInt())
1554 			{
1555 				highest = *j;
1556 			}
1557 		}
1558 	}
1559 	return highest;
1560 }
1561 
1562 /**
1563  * Gets the morale modifier for
1564  * - either XCom based on the highest ranked, living XCom unit,
1565  * - or the unit passed to this function.
1566  * @param unit Unit.
1567  * @return The morale modifier.
1568  */
getMoraleModifier(BattleUnit * unit)1569 int SavedBattleGame::getMoraleModifier(BattleUnit* unit)
1570 {
1571 	int result = 100;
1572 
1573 	if (unit == 0)
1574 	{
1575 		BattleUnit *leader = getHighestRankedXCom();
1576 		if (leader)
1577 		{
1578 			switch (leader->getRankInt())
1579 			{
1580 			case 5:
1581 				result += 25;
1582 			case 4:
1583 				result += 10;
1584 			case 3:
1585 				result += 5;
1586 			case 2:
1587 				result += 10;
1588 			default:
1589 				break;
1590 			}
1591 		}
1592 	}
1593 	else if (unit->getFaction() == FACTION_PLAYER)
1594 	{
1595 		switch (unit->getRankInt())
1596 		{
1597 		case 5:
1598 			result += 25;
1599 		case 4:
1600 			result += 20;
1601 		case 3:
1602 			result += 10;
1603 		case 2:
1604 			result += 20;
1605 		default:
1606 			break;
1607 		}
1608 	}
1609 	return result;
1610 }
1611 
1612 /**
1613  * Places a unit on or near a position.
1614  * @param unit The unit to place.
1615  * @param entryPoint The position around which to attempt to place the unit.
1616  * @return True if the unit was successfully placed.
1617  */
placeUnitNearPosition(BattleUnit * unit,Position entryPoint)1618 bool SavedBattleGame::placeUnitNearPosition(BattleUnit *unit, Position entryPoint)
1619 {
1620 	if (setUnitPosition(unit, entryPoint))
1621 	{
1622 		return true;
1623 	}
1624 
1625 	for (int dir = 0; dir <= 7; ++dir)
1626 	{
1627 		Position offset;
1628 		getPathfinding()->directionToVector(dir, &offset);
1629 		Tile *t = getTile(entryPoint + offset);
1630 		if (t && !getPathfinding()->isBlocked(getTile(entryPoint), t, dir, 0)
1631 			&& setUnitPosition(unit, entryPoint + offset))
1632 		{
1633 			return true;
1634 		}
1635 	}
1636 
1637 	if (unit->getArmor()->getMovementType() == MT_FLY)
1638 	{
1639 		Tile *t = getTile(entryPoint + Position(0, 0, 1));
1640 		if (t && t->hasNoFloor(getTile(entryPoint)) && setUnitPosition(unit, entryPoint + Position(0, 0, 1)))
1641 		{
1642 			return true;
1643 		}
1644 	}
1645 	return false;
1646 }
1647 
1648 /**
1649  * Resets the turn counter.
1650  */
resetTurnCounter()1651 void SavedBattleGame::resetTurnCounter()
1652 {
1653 	_turn = 1;
1654 }
1655 
1656 /**
1657  * Resets visibility of all the tiles on the map.
1658  */
resetTiles()1659 void SavedBattleGame::resetTiles()
1660 {
1661 	for (int i = 0; i != getMapSizeXYZ(); ++i)
1662 	{
1663 		_tiles[i]->setDiscovered(false, 0);
1664 		_tiles[i]->setDiscovered(false, 1);
1665 		_tiles[i]->setDiscovered(false, 2);
1666 	}
1667 }
1668 
1669 /**
1670  * @return the tilesearch vector for use in AI functions.
1671  */
getTileSearch()1672 const std::vector<Position> SavedBattleGame::getTileSearch()
1673 {
1674 	return _tileSearch;
1675 }
1676 
1677 /**
1678  * is the AI allowed to cheat?
1679  * @return true if cheating.
1680  */
isCheating()1681 bool SavedBattleGame::isCheating()
1682 {
1683 	return _cheating;
1684 }
1685 
1686 /**
1687  * Gets the TU reserved type.
1688  * @return A battleactiontype.
1689  */
getTUReserved() const1690 BattleActionType SavedBattleGame::getTUReserved() const
1691 {
1692 	return _tuReserved;
1693 }
1694 
1695 /**
1696  * Sets the TU reserved type.
1697  * @param reserved A battleactiontype.
1698  */
setTUReserved(BattleActionType reserved)1699 void SavedBattleGame::setTUReserved(BattleActionType reserved)
1700 {
1701 	_tuReserved = reserved;
1702 }
1703 
1704 /**
1705  * Gets the kneel reservation setting.
1706  * @return Should we reserve an extra 4 TUs to kneel?
1707  */
getKneelReserved() const1708 bool SavedBattleGame::getKneelReserved() const
1709 {
1710 	return _kneelReserved;
1711 }
1712 
1713 /**
1714  * Sets the kneel reservation setting.
1715  * @param reserved Should we reserve an extra 4 TUs to kneel?
1716  */
setKneelReserved(bool reserved)1717 void SavedBattleGame::setKneelReserved(bool reserved)
1718 {
1719 	_kneelReserved = reserved;
1720 }
1721 
1722 /**
1723  * Return a reference to the base module destruction map
1724  * this map contains information on how many destructible base modules
1725  * remain at any given grid reference in the basescape, using [x][y] format.
1726  * -1 for "no items" 0 for "destroyed" and any actual number represents how many left.
1727  * @return the base module damage map.
1728  */
getModuleMap()1729 std::vector< std::vector<std::pair<int, int> > > &SavedBattleGame::getModuleMap()
1730 {
1731 	return _baseModules;
1732 }
1733 /**
1734  * calculate the number of map modules remaining by counting the map objects
1735  * on the top floor who have the baseModule flag set. we store this data in the grid
1736  * as outlined in the comments above, in pairs representing intial and current values.
1737  */
calculateModuleMap()1738 void SavedBattleGame::calculateModuleMap()
1739 {
1740 	_baseModules.resize((_mapsize_x / 10), std::vector<std::pair<int, int> >((_mapsize_y / 10), std::make_pair(-1, -1)));
1741 
1742 	for (int x = 0; x != _mapsize_x; ++x)
1743 	{
1744 		for (int y = 0; y != _mapsize_y; ++y)
1745 		{
1746 			Tile *tile = getTile(Position(x,y,_mapsize_z-1));
1747 			if (tile && tile->getMapData(MapData::O_OBJECT) && tile->getMapData(MapData::O_OBJECT)->isBaseModule())
1748 			{
1749 				_baseModules[x/10][y/10].first += _baseModules[x/10][y/10].first > 0 ? 1 : 2;
1750 				_baseModules[x/10][y/10].second = _baseModules[x/10][y/10].first;
1751 			}
1752 		}
1753 	}
1754 }
1755 
1756 /**
1757  * get a pointer to the geoscape save
1758  * @return a pointer to the geoscape save.
1759  */
getGeoscapeSave()1760 SavedGame *SavedBattleGame::getGeoscapeSave()
1761 {
1762 	return _battleState->getGame()->getSavedGame();
1763 }
1764 }
1765