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