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 <fstream>
21 #include <sstream>
22 #include "BattlescapeGenerator.h"
23 #include "TileEngine.h"
24 #include "Inventory.h"
25 #include "../Savegame/SavedGame.h"
26 #include "../Savegame/SavedBattleGame.h"
27 #include "../Savegame/Tile.h"
28 #include "../Savegame/ItemContainer.h"
29 #include "../Savegame/Base.h"
30 #include "../Savegame/BaseFacility.h"
31 #include "../Savegame/Soldier.h"
32 #include "../Savegame/BattleUnit.h"
33 #include "../Savegame/BattleItem.h"
34 #include "../Savegame/Ufo.h"
35 #include "../Savegame/Craft.h"
36 #include "../Savegame/Node.h"
37 #include "../Engine/RNG.h"
38 #include "../Engine/Exception.h"
39 #include "../Ruleset/MapBlock.h"
40 #include "../Ruleset/MapDataSet.h"
41 #include "../Ruleset/RuleUfo.h"
42 #include "../Ruleset/RuleCraft.h"
43 #include "../Ruleset/RuleTerrain.h"
44 #include "../Ruleset/RuleInventory.h"
45 #include "../Ruleset/Ruleset.h"
46 #include "../Ruleset/MapData.h"
47 #include "../Ruleset/MCDPatch.h"
48 #include "../Ruleset/Armor.h"
49 #include "../Ruleset/Unit.h"
50 #include "../Ruleset/AlienRace.h"
51 #include "../Ruleset/AlienDeployment.h"
52 #include "../Ruleset/RuleBaseFacility.h"
53 #include "../Resource/XcomResourcePack.h"
54 #include "../Engine/Game.h"
55 #include "../Engine/Language.h"
56 #include "../Engine/CrossPlatform.h"
57 #include "../Engine/Options.h"
58 #include "../Savegame/Vehicle.h"
59 #include "../Savegame/TerrorSite.h"
60 #include "../Savegame/AlienBase.h"
61 #include "../Savegame/EquipmentLayoutItem.h"
62 #include "CivilianBAIState.h"
63 #include "AlienBAIState.h"
64 #include "Pathfinding.h"
65 
66 namespace OpenXcom
67 {
68 
69 /**
70  * Sets up a BattlescapeGenerator.
71  * @param game pointer to Game object.
72  */
BattlescapeGenerator(Game * game)73 BattlescapeGenerator::BattlescapeGenerator(Game *game) : _game(game), _save(game->getSavedGame()->getSavedBattle()), _res(_game->getResourcePack()), _craft(0), _ufo(0), _base(0), _terror(0), _alienBase(0), _terrain(0),
74 														 _mapsize_x(0), _mapsize_y(0), _mapsize_z(0), _worldTexture(0), _worldShade(0), _unitSequence(0), _craftInventoryTile(0), _alienRace(""), _alienItemLevel(0), _baseInventory(false), _craftX(0), _craftY(0), _craftZ(0)
75 {
76 	_allowAutoLoadout = !Options::disableAutoEquip;
77 }
78 
79 /**
80  * Deletes the BattlescapeGenerator.
81  */
~BattlescapeGenerator()82 BattlescapeGenerator::~BattlescapeGenerator()
83 {
84 
85 }
86 
87 /**
88  * Sets the XCom craft involved in the battle.
89  * @param craft Pointer to XCom craft.
90  */
setCraft(Craft * craft)91 void BattlescapeGenerator::setCraft(Craft *craft)
92 {
93 	_craft = craft;
94 	_craft->setInBattlescape(true);
95 }
96 
97 /**
98  * Sets the ufo involved in the battle.
99  * @param ufo Pointer to UFO.
100  */
setUfo(Ufo * ufo)101 void BattlescapeGenerator::setUfo(Ufo *ufo)
102 {
103 	_ufo = ufo;
104 	_ufo->setInBattlescape(true);
105 }
106 
107 /**
108  * Sets the world texture where a ufo crashed. This is used to determine the terrain.
109  * @param texture Texture id of the polygon on the globe.
110  */
setWorldTexture(int texture)111 void BattlescapeGenerator::setWorldTexture(int texture)
112 {
113 	if (texture < 0) texture = 0;
114 	_worldTexture = texture;
115 }
116 
117 /**
118  * Sets the world shade where a ufo crashed. This is used to determine the battlescape light level.
119  * @param shade Shade of the polygon on the globe.
120  */
setWorldShade(int shade)121 void BattlescapeGenerator::setWorldShade(int shade)
122 {
123 	if (shade > 15) shade = 15;
124 	if (shade < 0) shade = 0;
125 	_worldShade = shade;
126 }
127 
128 /**
129  * Sets the alien race on the mission. This is used to determine the various alien types to spawn.
130  * @param alienRace Alien (main) race.
131  */
setAlienRace(const std::string & alienRace)132 void BattlescapeGenerator::setAlienRace(const std::string &alienRace)
133 {
134 	_alienRace = alienRace;
135 }
136 
137 /**
138  * Sets the alien item level. This is used to determine how advanced the equipment of the aliens will be.
139  * note: this only applies to "New Battle" type games. we intentionally don't alter the month for those,
140  * because we're using monthsPassed -1 for new battle in other sections of code.
141  * - this value should be from 0 to the size of the itemLevel array in the ruleset (default 9).
142  * - at a certain number of months higher item levels appear more and more and lower ones will gradually disappear
143  * @param alienItemLevel AlienItemLevel.
144  */
setAlienItemlevel(int alienItemLevel)145 void BattlescapeGenerator::setAlienItemlevel(int alienItemLevel)
146 {
147 	_alienItemLevel = alienItemLevel;
148 }
149 
150 /**
151  * Sets the XCom base involved in the battle.
152  * @param base Pointer to XCom base.
153  */
setBase(Base * base)154 void BattlescapeGenerator::setBase(Base *base)
155 {
156 	_base = base;
157 	_base->setInBattlescape(true);
158 }
159 
160 /**
161  * Sets the terror site involved in the battle.
162  * @param terror Pointer to terror site.
163  */
setTerrorSite(TerrorSite * terror)164 void BattlescapeGenerator::setTerrorSite(TerrorSite *terror)
165 {
166 	_terror = terror;
167 	_terror->setInBattlescape(true);
168 }
169 
170 
171 /**
172  * Switches an existing battlescapesavegame to a new stage.
173  */
nextStage()174 void BattlescapeGenerator::nextStage()
175 {
176 	// kill all enemy units, or those not in endpoint area (if aborted)
177 	for (std::vector<BattleUnit*>::iterator j = _save->getUnits()->begin(); j != _save->getUnits()->end(); ++j)
178 	{
179 		if ((_game->getSavedGame()->getSavedBattle()->isAborted() && !(*j)->isInExitArea(END_POINT))
180 			|| (*j)->getOriginalFaction() == FACTION_HOSTILE)
181 		{
182 			(*j)->instaKill();
183 		}
184 		if ((*j)->getTile())
185 		{
186 			(*j)->getTile()->setUnit(0);
187 		}
188 		(*j)->setTile(0);
189 		(*j)->setPosition(Position(-1,-1,-1), false);
190 	}
191 
192 	while (_game->getSavedGame()->getSavedBattle()->getSide() != FACTION_PLAYER)
193 	{
194 		_game->getSavedGame()->getSavedBattle()->endTurn();
195 	}
196 	_save->resetTurnCounter();
197 
198 	AlienDeployment *ruleDeploy = _game->getRuleset()->getDeployment(_save->getMissionType());
199 	ruleDeploy->getDimensions(&_mapsize_x, &_mapsize_y, &_mapsize_z);
200 	size_t pick = RNG::generate(0, ruleDeploy->getTerrains().size() -1);
201 	_terrain = _game->getRuleset()->getTerrain(ruleDeploy->getTerrains().at(pick));
202 	_worldShade = ruleDeploy->getShade();
203 
204 	_save->initMap(_mapsize_x, _mapsize_y, _mapsize_z);
205 	generateMap();
206 	int highestSoldierID = 0;
207 	bool selectedFirstSoldier = false;
208 	for (std::vector<BattleUnit*>::iterator j = _save->getUnits()->begin(); j != _save->getUnits()->end(); ++j)
209 	{
210 		if ((*j)->getOriginalFaction() == FACTION_PLAYER)
211 		{
212 			if (!(*j)->isOut())
213 			{
214 				(*j)->convertToFaction(FACTION_PLAYER);
215 				(*j)->setTurnsSinceSpotted(255);
216 				(*j)->getVisibleTiles()->clear();
217 				if (!selectedFirstSoldier && (*j)->getGeoscapeSoldier())
218 				{
219 					_save->setSelectedUnit(*j);
220 					selectedFirstSoldier = true;
221 				}
222 				Node* node = _save->getSpawnNode(NR_XCOM, (*j));
223 				if (node)
224 				{
225 					_save->setUnitPosition((*j), node->getPosition());
226 					if (!_craftInventoryTile)
227 					{
228 						_craftInventoryTile = (*j)->getTile();
229 					}
230 					_craftInventoryTile->setUnit(*j);
231 					(*j)->setVisible(false);
232 					if ((*j)->getId() > highestSoldierID)
233 					{
234 						highestSoldierID = (*j)->getId();
235 					}
236 				}
237 				else if (placeUnitNearFriend(*j))
238 				{
239 					if ((*j)->getId() > highestSoldierID)
240 					{
241 						highestSoldierID = (*j)->getId();
242 					}
243 					_craftInventoryTile->setUnit(*j);
244 					(*j)->setVisible(false);
245 				}
246 			}
247 		}
248 	}
249 
250 	// remove all items not belonging to our soldiers from the map.
251 	for (std::vector<BattleItem*>::iterator j = _save->getItems()->begin(); j != _save->getItems()->end(); ++j)
252 	{
253 		if (!(*j)->getOwner() || (*j)->getOwner()->getId() > highestSoldierID)
254 		{
255 			(*j)->setTile(0);
256 		}
257 	}
258 	_unitSequence = _save->getUnits()->back()->getId() + 1;
259 	deployAliens(_game->getRuleset()->getAlienRace(_alienRace), ruleDeploy);
260 
261 	deployCivilians(ruleDeploy->getCivilians());
262 
263 	for (int i = 0; i < _save->getMapSizeXYZ(); ++i)
264 	{
265 		if (_save->getTiles()[i]->getMapData(MapData::O_FLOOR) &&
266 			(_save->getTiles()[i]->getMapData(MapData::O_FLOOR)->getSpecialType() == START_POINT ||
267 			(_save->getTiles()[i]->getPosition().z == 1 &&
268 			_save->getTiles()[i]->getMapData(MapData::O_FLOOR)->isGravLift() &&
269 			_save->getTiles()[i]->getMapData(MapData::O_OBJECT))))
270 				_save->getTiles()[i]->setDiscovered(true, 2);
271 	}
272 	_save->setGlobalShade(_worldShade);
273 	_save->getTileEngine()->calculateSunShading();
274 	_save->getTileEngine()->calculateTerrainLighting();
275 	_save->getTileEngine()->calculateUnitLighting();
276 	_save->getTileEngine()->recalculateFOV();
277 }
278 
279 /**
280  * Starts the generator; it fills up the battlescapesavegame with data.
281  */
run()282 void BattlescapeGenerator::run()
283 {
284 	AlienDeployment *ruleDeploy = _game->getRuleset()->getDeployment(_ufo?_ufo->getRules()->getType():_save->getMissionType());
285 
286 	ruleDeploy->getDimensions(&_mapsize_x, &_mapsize_y, &_mapsize_z);
287 
288 	_unitSequence = BattleUnit::MAX_SOLDIER_ID; // geoscape soldier IDs should stay below this number
289 
290 	if (ruleDeploy->getTerrains().empty())
291 	{
292 		double lat = 0;
293 		if (_ufo) lat = _ufo->getLatitude();
294 		_terrain = getTerrain(_worldTexture, lat);
295 	}
296 	else
297 	{
298 		size_t pick = RNG::generate(0, ruleDeploy->getTerrains().size() -1);
299 		_terrain = _game->getRuleset()->getTerrain(ruleDeploy->getTerrains().at(pick));
300 	}
301 
302 	if (ruleDeploy->getShade() != -1)
303 	{
304 		_worldShade = ruleDeploy->getShade();
305 	}
306 
307 	// creates the tile objects
308 	_save->initMap(_mapsize_x, _mapsize_y, _mapsize_z);
309 	_save->initUtilities(_res);
310 
311 	// lets generate the map now and store it inside the tile objects
312 	generateMap();
313 
314 	if (_craft != 0 || _base != 0)
315 	{
316 		deployXCOM();
317 	}
318 
319 	deployAliens(_game->getRuleset()->getAlienRace(_alienRace), ruleDeploy);
320 
321 	deployCivilians(ruleDeploy->getCivilians());
322 
323 	fuelPowerSources();
324 
325 	if (_save->getMissionType() ==  "STR_UFO_CRASH_RECOVERY")
326 	{
327 		explodePowerSources();
328 	}
329 
330 	if (_save->getMissionType() == "STR_BASE_DEFENSE")
331 	{
332 		for (int i = 0; i < _save->getMapSizeXYZ(); ++i)
333 		{
334 			_save->getTiles()[i]->setDiscovered(true, 2);
335 		}
336 
337 		_save->calculateModuleMap();
338 	}
339 
340 	if (_save->getMissionType() == "STR_ALIEN_BASE_ASSAULT" || _save->getMissionType() == "STR_MARS_THE_FINAL_ASSAULT")
341 	{
342 		for (int i = 0; i < _save->getMapSizeXYZ(); ++i)
343 		{
344 			if (_save->getTiles()[i]->getMapData(MapData::O_FLOOR) &&
345 				(_save->getTiles()[i]->getMapData(MapData::O_FLOOR)->getSpecialType() == START_POINT ||
346 				(_save->getTiles()[i]->getPosition().z == 1 &&
347 				_save->getTiles()[i]->getMapData(MapData::O_FLOOR)->isGravLift() &&
348 				_save->getTiles()[i]->getMapData(MapData::O_OBJECT))))
349 				_save->getTiles()[i]->setDiscovered(true, 2);
350 		}
351 	}
352 
353 	// set shade (alien bases are a little darker, sites depend on worldshade)
354 	_save->setGlobalShade(_worldShade);
355 
356 	_save->getTileEngine()->calculateSunShading();
357 	_save->getTileEngine()->calculateTerrainLighting();
358 	_save->getTileEngine()->calculateUnitLighting();
359 	_save->getTileEngine()->recalculateFOV();
360 }
361 
362 /**
363 * Deploys all the X-COM units and equipment based
364 * on the Geoscape base / craft.
365 * @param inventoryTile The tile to place all the extra equipment on.
366 */
deployXCOM()367 void BattlescapeGenerator::deployXCOM()
368 {
369 	RuleInventory *ground = _game->getRuleset()->getInventory("STR_GROUND");
370 
371 	if (_craft != 0)
372 		_base = _craft->getBase();
373 
374 	// add vehicles that are in the craft - a vehicle is actually an item, which you will never see as it is converted to a unit
375 	// however the item itself becomes the weapon it "holds".
376 	if (!_baseInventory)
377 	{
378 		if (_craft != 0)
379 		{
380 			for (std::vector<Vehicle*>::iterator i = _craft->getVehicles()->begin(); i != _craft->getVehicles()->end(); ++i)
381 			{
382 				BattleUnit *unit = addXCOMVehicle(*i);
383 				if (unit && !_save->getSelectedUnit())
384 					_save->setSelectedUnit(unit);
385 			}
386 		}
387 		else if (_base != 0)
388 		{
389 			// add vehicles that are in the base inventory
390 			for (std::vector<Vehicle*>::iterator i = _base->getVehicles()->begin(); i != _base->getVehicles()->end(); ++i)
391 			{
392 				BattleUnit *unit = addXCOMVehicle(*i);
393 				if (unit && !_save->getSelectedUnit())
394 					_save->setSelectedUnit(unit);
395 			}
396 		}
397 	}
398 
399 	// add soldiers that are in the craft or base
400 	for (std::vector<Soldier*>::iterator i = _base->getSoldiers()->begin(); i != _base->getSoldiers()->end(); ++i)
401 	{
402 		if ((_craft != 0 && (*i)->getCraft() == _craft) ||
403 			(_craft == 0 && (*i)->getWoundRecovery() == 0 && ((*i)->getCraft() == 0 || (*i)->getCraft()->getStatus() != "STR_OUT")))
404 		{
405 			BattleUnit *unit = addXCOMUnit(new BattleUnit(*i, FACTION_PLAYER));
406 			if (unit && !_save->getSelectedUnit())
407 				_save->setSelectedUnit(unit);
408 		}
409 	}
410 
411 	// maybe we should assign all units to the first tile of the skyranger before the inventory pre-equip and then reassign them to their correct tile afterwards?
412 	// fix: make them invisible, they are made visible afterwards.
413 	for (std::vector<BattleUnit*>::iterator i = _save->getUnits()->begin(); i != _save->getUnits()->end(); ++i)
414 	{
415 		if ((*i)->getFaction() == FACTION_PLAYER)
416 		{
417 			_craftInventoryTile->setUnit(*i);
418 			(*i)->setVisible(false);
419 		}
420 	}
421 
422 	if (_craft != 0)
423 	{
424 		// add items that are in the craft
425 		for (std::map<std::string, int>::iterator i = _craft->getItems()->getContents()->begin(); i != _craft->getItems()->getContents()->end(); ++i)
426 		{
427 			for (int count = 0; count < i->second; count++)
428 			{
429 				_craftInventoryTile->addItem(new BattleItem(_game->getRuleset()->getItem(i->first), _save->getCurrentItemId()),	ground);
430 			}
431 		}
432 	}
433 	else
434 	{
435 		// add items that are in the base
436 		for (std::map<std::string, int>::iterator i = _base->getItems()->getContents()->begin(); i != _base->getItems()->getContents()->end();)
437 		{
438 			// only put items in the battlescape that make sense (when the item got a sprite, it's probably ok)
439 			RuleItem *rule = _game->getRuleset()->getItem(i->first);
440 			if (rule->getBigSprite() > -1 && rule->getBattleType() != BT_NONE && rule->getBattleType() != BT_CORPSE && !rule->isFixed() && _game->getSavedGame()->isResearched(rule->getRequirements()))
441 			{
442 				for (int count = 0; count < i->second; count++)
443 				{
444 					_craftInventoryTile->addItem(new BattleItem(_game->getRuleset()->getItem(i->first), _save->getCurrentItemId()), ground);
445 				}
446 				std::map<std::string, int>::iterator tmp = i;
447 				++i;
448 				_base->getItems()->removeItem(tmp->first, tmp->second);
449 			}
450 			else
451 			{
452 				++i;
453 			}
454 		}
455 		// add items from crafts in base
456 		for (std::vector<Craft*>::iterator c = _base->getCrafts()->begin(); c != _base->getCrafts()->end(); ++c)
457 		{
458 			if ((*c)->getStatus() == "STR_OUT")
459 				continue;
460 			for (std::map<std::string, int>::iterator i = (*c)->getItems()->getContents()->begin(); i != (*c)->getItems()->getContents()->end(); ++i)
461 			{
462 				for (int count = 0; count < i->second; count++)
463 				{
464 					_craftInventoryTile->addItem(new BattleItem(_game->getRuleset()->getItem(i->first), _save->getCurrentItemId()), ground);
465 				}
466 			}
467 		}
468 	}
469 
470 	// equip soldiers based on equipment-layout
471 	for (std::vector<BattleItem*>::iterator i = _craftInventoryTile->getInventory()->begin(); i != _craftInventoryTile->getInventory()->end(); ++i)
472 	{
473 		// don't let the soldiers take extra ammo yet
474 		if ((*i)->getRules()->getBattleType() == BT_AMMO)
475 			continue;
476 		placeItemByLayout(*i);
477 	}
478 
479 	// load weapons before loadouts take extra clips.
480 	loadWeapons();
481 
482 	for (std::vector<BattleItem*>::iterator i = _craftInventoryTile->getInventory()->begin(); i != _craftInventoryTile->getInventory()->end(); ++i)
483 	{
484 		// we only need to distribute extra ammo at this point.
485 		if ((*i)->getRules()->getBattleType() != BT_AMMO)
486 			continue;
487 		placeItemByLayout(*i);
488 	}
489 
490 
491 	// auto-equip soldiers (only soldiers without layout)
492 	for (int pass = 0; pass != 4; ++pass)
493 	{
494 		for (std::vector<BattleItem*>::iterator j = _craftInventoryTile->getInventory()->begin(); j != _craftInventoryTile->getInventory()->end();)
495 		{
496 			if ((*j)->getSlot() == ground)
497 			{
498 				bool add = false;
499 
500 				switch (pass)
501 				{
502 				// priority 1: rifles.
503 				case 0:
504 					add = (*j)->getRules()->isRifle();
505 					break;
506 				// priority 2: pistols (assuming no rifles were found).
507 				case 1:
508 					add = (*j)->getRules()->isPistol();
509 					break;
510 				// priority 3: ammunition.
511 				case 2:
512 					add = (*j)->getRules()->getBattleType() == BT_AMMO;
513 					break;
514 				// priority 4: leftovers.
515 				case 3:
516 					add = !(*j)->getRules()->isPistol() &&
517 							!(*j)->getRules()->isRifle() &&
518 							((*j)->getRules()->getBattleType() != BT_FLARE || _worldShade >= 9);
519 					break;
520 				default:
521 					break;
522 				}
523 
524 				if (add)
525 				{
526 					for (std::vector<BattleUnit*>::iterator i = _save->getUnits()->begin(); i != _save->getUnits()->end(); ++i)
527 					{
528 						if (!(*i)->hasInventory() || !(*i)->getGeoscapeSoldier() || !(*i)->getGeoscapeSoldier()->getEquipmentLayout()->empty())
529 						{
530 							continue;
531 						}
532 						// let's not be greedy, we'll only take a second extra clip
533 						// if everyone else has had a chance to take a first.
534 						bool allowSecondClip = (pass == 3);
535 
536 						if (addItem(*j, *i, allowSecondClip))
537 						{
538 							j = _craftInventoryTile->getInventory()->erase(j);
539 							add = false;
540 							break;
541 						}
542 					}
543 					if (!add)
544 					{
545 						continue;
546 					}
547 				}
548 			}
549 			++j;
550 		}
551 	}
552 	// clean up moved items
553 	for (std::vector<BattleItem*>::iterator i = _craftInventoryTile->getInventory()->begin(); i != _craftInventoryTile->getInventory()->end();)
554 	{
555 		if ((*i)->getSlot() != ground)
556 		{
557 			i = _craftInventoryTile->getInventory()->erase(i);
558 		}
559 		else
560 		{
561 			_save->getItems()->push_back(*i);
562 			++i;
563 		}
564 	}
565 }
566 
567 /**
568  * Adds an XCom vehicle to the game.
569  * Sets the correct turret depending on the ammo type.
570  * @param v Pointer to the Vehicle.
571  * @return Pointer to the spawned unit.
572  */
addXCOMVehicle(Vehicle * v)573 BattleUnit *BattlescapeGenerator::addXCOMVehicle(Vehicle *v)
574 {
575 	std::string vehicle = v->getRules()->getType();
576 	Unit *rule = _game->getRuleset()->getUnit(vehicle);
577 	BattleUnit *unit = addXCOMUnit(new BattleUnit(rule, FACTION_PLAYER, _unitSequence++, _game->getRuleset()->getArmor(rule->getArmor()), 0));
578 	if (unit)
579 	{
580 		BattleItem *item = new BattleItem(_game->getRuleset()->getItem(vehicle), _save->getCurrentItemId());
581 		addItem(item, unit);
582 		if(!v->getRules()->getCompatibleAmmo()->empty())
583 		{
584 			std::string ammo = v->getRules()->getCompatibleAmmo()->front();
585 			BattleItem *ammoItem = new BattleItem(_game->getRuleset()->getItem(ammo), _save->getCurrentItemId());
586 			addItem(ammoItem, unit);
587 			ammoItem->setAmmoQuantity(v->getAmmo());
588 		}
589 		unit->setTurretType(v->getRules()->getTurretType());
590 	}
591 	return unit;
592 }
593 
594 /**
595  * Adds a soldier to the game and places him on a free spawnpoint.
596  * Spawnpoints are either tiles in case of an XCom craft that landed.
597  * Or they are mapnodes in case there's no craft.
598  * @param soldier Pointer to the Soldier.
599  * @return Pointer to the spawned unit.
600  */
addXCOMUnit(BattleUnit * unit)601 BattleUnit *BattlescapeGenerator::addXCOMUnit(BattleUnit *unit)
602 {
603 //	unit->setId(_unitCount++);
604 
605 	if (_craft == 0 || _save->getMissionType() == "STR_ALIEN_BASE_ASSAULT" || _save->getMissionType() == "STR_MARS_THE_FINAL_ASSAULT")
606 	{
607 		Node* node = _save->getSpawnNode(NR_XCOM, unit);
608 		if (node)
609 		{
610 			_save->setUnitPosition(unit, node->getPosition());
611 			_craftInventoryTile = _save->getTile(node->getPosition());
612 			unit->setDirection(RNG::generate(0,7));
613 			_save->getUnits()->push_back(unit);
614 			_save->getTileEngine()->calculateFOV(unit);
615 			unit->deriveRank();
616 			return unit;
617 		}
618 		else if (_save->getMissionType() != "STR_BASE_DEFENSE")
619 		{
620 			if (placeUnitNearFriend(unit))
621 			{
622 				_craftInventoryTile = _save->getTile(unit->getPosition());
623 				unit->setDirection(RNG::generate(0,7));
624 				_save->getUnits()->push_back(unit);
625 				_save->getTileEngine()->calculateFOV(unit);
626 				unit->deriveRank();
627 				return unit;
628 			}
629 		}
630 	}
631 	else if (_craft && !_craft->getRules()->getDeployment().empty() && !_baseInventory)
632 	{
633 		for (std::vector<std::vector<int> >::const_iterator i = _craft->getRules()->getDeployment().begin(); i != _craft->getRules()->getDeployment().end(); ++i)
634 		{
635 			Position pos = Position((*i)[0] + (_craftX * 10), (*i)[1] + (_craftY * 10), (*i)[2] + _craftZ);
636 			int dir = (*i)[3];
637 
638 			if (canPlaceXCOMUnit(_save->getTile(pos)))
639 			{
640 				if (_save->setUnitPosition(unit, pos))
641 				{
642 					_save->getUnits()->push_back(unit);
643 					unit->setDirection(dir);
644 					unit->deriveRank();
645 					return unit;
646 				}
647 			}
648 		}
649 	}
650 	else
651 	{
652 		for (int i = 0; i < _mapsize_x * _mapsize_y * _mapsize_z; i++)
653 		{
654 			if (canPlaceXCOMUnit(_save->getTiles()[i]))
655 			{
656 				if (_save->setUnitPosition(unit, _save->getTiles()[i]->getPosition()))
657 				{
658 					_save->getUnits()->push_back(unit);
659 					unit->deriveRank();
660 					return unit;
661 				}
662 			}
663 		}
664 	}
665 	delete unit;
666 	return 0;
667 }
668 
669 /**
670  * Checks if a soldier/tank can be placed on a given tile.
671  * @param tile the given tile.
672  * @return whether the unit can be placed here.
673  */
canPlaceXCOMUnit(Tile * tile)674 bool BattlescapeGenerator::canPlaceXCOMUnit(Tile *tile)
675 {
676 	// to spawn an xcom soldier, there has to be a tile, with a floor, with the starting point attribute and no object in the way
677 	if (tile &&
678 		tile->getMapData(MapData::O_FLOOR) &&
679 		tile->getMapData(MapData::O_FLOOR)->getSpecialType() == START_POINT &&
680 		!tile->getMapData(MapData::O_OBJECT) &&
681 		tile->getMapData(MapData::O_FLOOR)->getTUCost(MT_WALK) < 255)
682 	{
683 		if (_craftInventoryTile == 0)
684 			_craftInventoryTile = tile;
685 
686 		return true;
687 	}
688 	return false;
689 }
690 
691 /**
692  * Deploys the aliens, according to the alien deployment rules.
693  * @param race Pointer to the alien race.
694  * @param deployment Pointer to the deployment rules.
695  */
deployAliens(AlienRace * race,AlienDeployment * deployment)696 void BattlescapeGenerator::deployAliens(AlienRace *race, AlienDeployment *deployment)
697 {
698 	int month;
699 	if (_game->getSavedGame()->getMonthsPassed() != -1)
700 	{
701 		month =
702 		((size_t) _game->getSavedGame()->getMonthsPassed()) > _game->getRuleset()->getAlienItemLevels().size() - 1 ?  // if
703 		_game->getRuleset()->getAlienItemLevels().size() - 1 :  // then
704 		_game->getSavedGame()->getMonthsPassed() ;  // else
705 	}
706 	else
707 	{
708 		month = _alienItemLevel;
709 	}
710 	for (std::vector<DeploymentData>::iterator d = deployment->getDeploymentData()->begin(); d != deployment->getDeploymentData()->end(); ++d)
711 	{
712 		std::string alienName = race->getMember((*d).alienRank);
713 
714 		int quantity;
715 
716 		if (_game->getSavedGame()->getDifficulty() < DIFF_VETERAN)
717 			quantity = (*d).lowQty + RNG::generate(0, (*d).dQty); // beginner/experienced
718 		else if (_game->getSavedGame()->getDifficulty() < DIFF_SUPERHUMAN)
719 			quantity = (*d).lowQty+(((*d).highQty-(*d).lowQty)/2) + RNG::generate(0, (*d).dQty); // veteran/genius
720 		else
721 			quantity = (*d).highQty + RNG::generate(0, (*d).dQty); // super (and beyond?)
722 
723 		for (int i = 0; i < quantity; i++)
724 		{
725 			bool outside = RNG::generate(0,99) < (*d).percentageOutsideUfo;
726 			if (_ufo == 0)
727 				outside = false;
728 			Unit *rule = _game->getRuleset()->getUnit(alienName);
729 			BattleUnit *unit = addAlien(rule, (*d).alienRank, outside);
730 			int itemLevel = _game->getRuleset()->getAlienItemLevels().at(month).at(RNG::generate(0,9));
731 			if (unit)
732 			{
733 				// terrorist alien's equipment is a special case - they are fitted with a weapon which is the alien's name with suffix _WEAPON
734 				if (rule->isLivingWeapon())
735 				{
736 					std::string terroristWeapon = rule->getRace().substr(4);
737 					terroristWeapon += "_WEAPON";
738 					RuleItem *ruleItem = _game->getRuleset()->getItem(terroristWeapon);
739 					if (ruleItem)
740 					{
741 						BattleItem *item = new BattleItem(ruleItem, _save->getCurrentItemId());
742 						if (!addItem(item, unit))
743 						{
744 							delete item;
745 						}
746 						else
747 						{
748 							unit->setTurretType(item->getRules()->getTurretType());
749 						}
750 					}
751 				}
752 				else
753 				{
754 					for (std::vector<std::string>::iterator it = (*d).itemSets.at(itemLevel).items.begin(); it != (*d).itemSets.at(itemLevel).items.end(); ++it)
755 					{
756 						RuleItem *ruleItem = _game->getRuleset()->getItem((*it));
757 						if (ruleItem)
758 						{
759 							BattleItem *item = new BattleItem(ruleItem, _save->getCurrentItemId());
760 							if (!addItem(item, unit))
761 							{
762 								delete item;
763 							}
764 						}
765 					}
766 				}
767 			}
768 		}
769 	}
770 }
771 
772 
773 
774 /**
775  * Adds an alien to the game and places him on a free spawnpoint.
776  * @param rules Pointer to the Unit which holds info about the alien .
777  * @param alienRank The rank of the alien, used for spawn point search.
778  * @param outside Whether the alien should spawn outside or inside the UFO.
779  * @return Pointer to the created unit.
780  */
addAlien(Unit * rules,int alienRank,bool outside)781 BattleUnit *BattlescapeGenerator::addAlien(Unit *rules, int alienRank, bool outside)
782 {
783 	int difficulty = (int)(_game->getSavedGame()->getDifficulty());
784 	BattleUnit *unit = new BattleUnit(rules, FACTION_HOSTILE, _unitSequence++, _game->getRuleset()->getArmor(rules->getArmor()), difficulty);
785 	Node *node = 0;
786 
787 	/* following data is the order in which certain alien ranks spawn on certain node ranks */
788 	/* note that they all can fall back to rank 0 nodes - which is scout (outside ufo) */
789 
790 	for (int i = 0; i < 7 && node == 0; i++)
791 	{
792 		if (outside)
793 			node = _save->getSpawnNode(0, unit); // when alien is instructed to spawn outside, we only look for node 0 spawnpoints
794 		else
795 			node = _save->getSpawnNode(Node::nodeRank[alienRank][i], unit);
796 	}
797 
798 	if (node && _save->setUnitPosition(unit, node->getPosition()))
799 	{
800 		unit->setAIState(new AlienBAIState(_game->getSavedGame()->getSavedBattle(), unit, node));
801 		unit->setRankInt(alienRank);
802 		int dir = _save->getTileEngine()->faceWindow(node->getPosition());
803 		Position craft = _game->getSavedGame()->getSavedBattle()->getUnits()->at(0)->getPosition();
804 		if (_save->getTileEngine()->distance(node->getPosition(), craft) <= 20 && RNG::percent(20 * difficulty))
805 			dir = unit->directionTo(craft);
806 		if (dir != -1)
807 			unit->setDirection(dir);
808 		else
809 			unit->setDirection(RNG::generate(0,7));
810 
811 		if (!difficulty)
812 		{
813 			unit->halveArmor();
814 		}
815 
816 		// we only add a unit if it has a node to spawn on.
817 		// (stops them spawning at 0,0,0)
818 		_save->getUnits()->push_back(unit);
819 	}
820 	else
821 	{
822 		delete unit;
823 		unit = 0;
824 	}
825 
826 	return unit;
827 }
828 
829 /**
830  * Adds a civilian to the game and places him on a free spawnpoint.
831  * @param rules Pointer to the Unit which holds info about the civilian.
832  * @return Pointer to the created unit.
833  */
addCivilian(Unit * rules)834 BattleUnit *BattlescapeGenerator::addCivilian(Unit *rules)
835 {
836 	BattleUnit *unit = new BattleUnit(rules, FACTION_NEUTRAL, _unitSequence++, _game->getRuleset()->getArmor(rules->getArmor()), 0);
837 	Node *node = _save->getSpawnNode(0, unit);
838 
839 	if (node)
840 	{
841 		_save->setUnitPosition(unit, node->getPosition());
842 		unit->setAIState(new CivilianBAIState(_game->getSavedGame()->getSavedBattle(), unit, node));
843 		unit->setDirection(RNG::generate(0,7));
844 
845 		// we only add a unit if it has a node to spawn on.
846 		// (stops them spawning at 0,0,0)
847 		_save->getUnits()->push_back(unit);
848 	}
849 	else if (placeUnitNearFriend(unit))
850 	{
851 		unit->setAIState(new CivilianBAIState(_game->getSavedGame()->getSavedBattle(), unit, node));
852 		unit->setDirection(RNG::generate(0,7));
853 		_save->getUnits()->push_back(unit);
854 	}
855 	else
856 	{
857 		delete unit;
858 		unit = 0;
859 	}
860 
861 	return unit;
862 }
863 
864 /**
865  * Places an item on an XCom soldier based on equipment layout.
866  * @param item Pointer to the Item.
867  * @return Pointer to the Item.
868  */
placeItemByLayout(BattleItem * item)869 bool BattlescapeGenerator::placeItemByLayout(BattleItem *item)
870 {
871 	RuleInventory *ground = _game->getRuleset()->getInventory("STR_GROUND");
872 	if (item->getSlot() == ground)
873 	{
874 		bool loaded;
875 		RuleInventory *righthand = _game->getRuleset()->getInventory("STR_RIGHT_HAND");
876 
877 		// find the first soldier with a matching layout-slot
878 		for (std::vector<BattleUnit*>::iterator i = _save->getUnits()->begin(); i != _save->getUnits()->end(); ++i)
879 		{
880 			// skip the vehicles, we need only X-Com soldiers WITH equipment-layout
881 			if ((*i)->getArmor()->getSize() > 1 || !(*i)->getGeoscapeSoldier() || (*i)->getGeoscapeSoldier()->getEquipmentLayout()->empty())
882 			{
883 				continue;
884 			}
885 
886 			// find the first matching layout-slot which is not already occupied
887 			std::vector<EquipmentLayoutItem*> *layoutItems = (*i)->getGeoscapeSoldier()->getEquipmentLayout();
888 			for (std::vector<EquipmentLayoutItem*>::iterator j = layoutItems->begin(); j != layoutItems->end(); ++j)
889 			{
890 				if (item->getRules()->getType() != (*j)->getItemType()
891 				|| (*i)->getItem((*j)->getSlot(), (*j)->getSlotX(), (*j)->getSlotY())) continue;
892 
893 				if ((*j)->getAmmoItem() == "NONE")
894 				{
895 					loaded = true;
896 				}
897 				else
898 				{
899 					loaded = false;
900 					// maybe we find the layout-ammo on the ground to load it with
901 					for (std::vector<BattleItem*>::iterator k = _craftInventoryTile->getInventory()->begin(); (!loaded) && k != _craftInventoryTile->getInventory()->end(); ++k)
902 					{
903 						if ((*k)->getRules()->getType() == (*j)->getAmmoItem() && (*k)->getSlot() == ground
904 						&& item->setAmmoItem((*k)) == 0)
905 						{
906 							_save->getItems()->push_back(*k);
907 							(*k)->setSlot(righthand);
908 							loaded = true;
909 							// note: soldier is not owner of the ammo, we are using this fact when saving equipments
910 						}
911 					}
912 				}
913 				// only place the weapon onto the soldier when it's loaded with its layout-ammo (if any)
914 				if (loaded)
915 				{
916 					item->moveToOwner((*i));
917 					item->setSlot(_game->getRuleset()->getInventory((*j)->getSlot()));
918 					item->setSlotX((*j)->getSlotX());
919 					item->setSlotY((*j)->getSlotY());
920 					if (Options::includePrimeStateInSavedLayout &&
921 						(item->getRules()->getBattleType() == BT_GRENADE ||
922 						item->getRules()->getBattleType() == BT_PROXIMITYGRENADE))
923 					{
924 						item->setFuseTimer((*j)->getFuseTimer());
925 					}
926 					_save->getItems()->push_back(item);
927 					return true;
928 				}
929 			}
930 		}
931 	}
932 	return false;
933 }
934 
935 /**
936  * Adds an item to an XCom soldier (auto-equip).
937  * @param item Pointer to the Item.
938  * @param unit Pointer to the Unit.
939  * @param allowSecondClip allow the unit to take a second clip or not. (only applies to xcom soldiers, aliens are allowed regardless of this flag)
940  * @return if the item was placed or not.
941  */
addItem(BattleItem * item,BattleUnit * unit,bool allowSecondClip)942 bool BattlescapeGenerator::addItem(BattleItem *item, BattleUnit *unit, bool allowSecondClip)
943 {
944 	RuleInventory *ground = _game->getRuleset()->getInventory("STR_GROUND");
945 	RuleInventory *rightHand = _game->getRuleset()->getInventory("STR_RIGHT_HAND");
946 	bool placed = false;
947 	bool loaded = false;
948 	BattleItem *weapon = unit->getItem("STR_RIGHT_HAND");
949 	int weight = 0;
950 
951 	// tanks and aliens don't care about weight or multiple items,
952 	// their loadouts are defined in the rulesets and more or less set in stone.
953 	if (unit->getFaction() == FACTION_PLAYER && unit->hasInventory())
954 	{
955 		weight = unit->getCarriedWeight() + item->getRules()->getWeight();
956 		if (item->getAmmoItem() && item->getAmmoItem() != item)
957 		{
958 			weight += item->getAmmoItem()->getRules()->getWeight();
959 		}
960 		// allow all weapons to be loaded by avoiding this check,
961 		// they'll return false later anyway if the unit has something in his hand.
962 		if (item->getRules()->getCompatibleAmmo()->empty())
963 		{
964 			int tally = 0;
965 			for (std::vector<BattleItem*>::iterator i = unit->getInventory()->begin(); i != unit->getInventory()->end(); ++i)
966 			{
967 				if (item->getRules()->getType() == (*i)->getRules()->getType())
968 				{
969 					if (allowSecondClip && item->getRules()->getBattleType() == BT_AMMO)
970 					{
971 						tally++;
972 						if (tally == 2)
973 						{
974 							return false;
975 						}
976 					}
977 					else
978 					{
979 						// we already have one, thanks.
980 						return false;
981 					}
982 				}
983 			}
984 		}
985 	}
986 
987 	switch (item->getRules()->getBattleType())
988 	{
989 	case BT_FIREARM:
990 	case BT_MELEE:
991 		if (item->getAmmoItem() || unit->getFaction() != FACTION_PLAYER || !unit->hasInventory())
992 		{
993 			loaded = true;
994 		}
995 
996 		if (loaded && (unit->getGeoscapeSoldier() == 0 || _allowAutoLoadout))
997 		{
998 			if (!unit->getItem("STR_RIGHT_HAND") && unit->getStats()->strength * 0.66 >= weight) // weight is always considered 0 for aliens
999 			{
1000 				item->moveToOwner(unit);
1001 				item->setSlot(rightHand);
1002 				placed = true;
1003 			}
1004 		}
1005 		break;
1006 	case BT_AMMO:
1007 		// no weapon, or our weapon takes no ammo, or this ammo isn't compatible.
1008 		// we won't be needing this. move on.
1009 		if (!weapon || weapon->getRules()->getCompatibleAmmo()->empty() ||
1010 			std::find(weapon->getRules()->getCompatibleAmmo()->begin(),
1011 			weapon->getRules()->getCompatibleAmmo()->end(),
1012 			item->getRules()->getType()) == weapon->getRules()->getCompatibleAmmo()->end())
1013 		{
1014 			break;
1015 		}
1016 		// xcom weapons will already be loaded, aliens and tanks, however, get their ammo added afterwards.
1017 		// so let's try to load them here.
1018 		if ((weapon->getRules()->isFixed() || unit->getFaction() != FACTION_PLAYER) &&
1019 			!weapon->getAmmoItem() &&
1020 			weapon->setAmmoItem(item) == 0)
1021 		{
1022 			item->setSlot(rightHand);
1023 			placed = true;
1024 			break;
1025 		}
1026 	default:
1027 		if ((unit->getGeoscapeSoldier() == 0 || _allowAutoLoadout))
1028 		{
1029 			if (unit->getStats()->strength >= weight) // weight is always considered 0 for aliens
1030 			{
1031 				for (std::vector<std::string>::const_iterator i = _game->getRuleset()->getInvsList().begin(); i != _game->getRuleset()->getInvsList().end() && !placed; ++i)
1032 				{
1033 					RuleInventory *slot = _game->getRuleset()->getInventory(*i);
1034 					if (slot->getType() == INV_SLOT)
1035 					{
1036 						for (std::vector<RuleSlot>::iterator j = slot->getSlots()->begin(); j != slot->getSlots()->end() && !placed; ++j)
1037 						{
1038 							if (!Inventory::overlapItems(unit, item, slot, j->x, j->y) && slot->fitItemInSlot(item->getRules(), j->x, j->y))
1039 							{
1040 								item->moveToOwner(unit);
1041 								item->setSlot(slot);
1042 								item->setSlotX(j->x);
1043 								item->setSlotY(j->y);
1044 								placed = true;
1045 								break;
1046 							}
1047 						}
1048 					}
1049 				}
1050 			}
1051 		}
1052 	break;
1053 	}
1054 
1055 	if (placed)
1056 	{
1057 		_save->getItems()->push_back(item);
1058 	}
1059 	item->setXCOMProperty(unit->getFaction() == FACTION_PLAYER);
1060 
1061 	return placed;
1062 }
1063 
1064 /**
1065  * Generates a map (set of tiles) for a new battlescape game.
1066  */
generateMap()1067 void BattlescapeGenerator::generateMap()
1068 {
1069 	int x = 0, y = 0;
1070 	int blocksToDo = 0;
1071 	std::vector< std::vector<MapBlock*> > blocks;
1072 	std::vector< std::vector<bool> > storageBlocks;
1073 	std::vector< std::vector<bool> > landingzone;
1074 	std::vector< std::vector<int> > segments;
1075 	int ufoX = 0, ufoY = 0;
1076 	bool placed = false;
1077 
1078 	MapBlock* dummy = new MapBlock("dummy", 0, 0, MT_DEFAULT);
1079 	MapBlock* craftMap = 0;
1080 	MapBlock* ufoMap = 0;
1081 
1082 	int mapDataSetIDOffset = 0;
1083 	int craftDataSetIDOffset = 0;
1084 
1085 	blocks.resize((_mapsize_x / 10), std::vector<MapBlock*>((_mapsize_y / 10)));
1086 	landingzone.resize((_mapsize_x / 10), std::vector<bool>((_mapsize_y / 10),false));
1087 	storageBlocks.resize((_mapsize_x / 10), std::vector<bool>((_mapsize_y / 10),false));
1088 	segments.resize((_mapsize_x / 10), std::vector<int>((_mapsize_y / 10),0));
1089 
1090 	blocksToDo = (_mapsize_x / 10) * (_mapsize_y / 10);
1091 
1092 	/* Determine UFO landingzone (do this first because ufo is generally bigger) */
1093 	if (_ufo != 0)
1094 	{
1095 		// pick a random ufo mapblock, can have all kinds of sizes
1096 		ufoMap = _ufo->getRules()->getBattlescapeTerrainData()->getRandomMapBlock(999, MT_DEFAULT);
1097 
1098 		ufoX = RNG::generate(0, (_mapsize_x / 10) - ufoMap->getSizeX() / 10);
1099 		ufoY = RNG::generate(0, (_mapsize_y / 10) - ufoMap->getSizeY() / 10);
1100 
1101 		for (int i = 0; i < ufoMap->getSizeX() / 10; ++i)
1102 		{
1103 			for (int j = 0; j < ufoMap->getSizeY() / 10; ++j)
1104 			{
1105 				landingzone[ufoX + i][ufoY + j] = true;
1106 				blocks[ufoX + i][ufoY + j] = _terrain->getRandomMapBlock(10, MT_LANDINGZONE);
1107 				blocksToDo--;
1108 			}
1109 		}
1110 	}
1111 
1112 	/* Determine Craft landingzone */
1113 	/* alien base assault has no craft landing zone */
1114 	if (_craft != 0 && (_save->getMissionType() != "STR_ALIEN_BASE_ASSAULT") && ( _save->getMissionType() != "STR_MARS_THE_FINAL_ASSAULT"))
1115 	{
1116 		// pick a random craft mapblock, can have all kinds of sizes
1117 		craftMap = _craft->getRules()->getBattlescapeTerrainData()->getRandomMapBlock(999, MT_DEFAULT);
1118 		while (!placed)
1119 		{
1120 			_craftX = RNG::generate(0, (_mapsize_x/10)- craftMap->getSizeX() / 10);
1121 			_craftY = RNG::generate(0, (_mapsize_y/10)- craftMap->getSizeY() / 10);
1122 			placed = true;
1123 			// check if this place is ok
1124 			for (int i = 0; i < craftMap->getSizeX() / 10; ++i)
1125 			{
1126 				for (int j = 0; j < craftMap->getSizeY() / 10; ++j)
1127 				{
1128 					if (landingzone[_craftX + i][_craftY + j])
1129 					{
1130 						placed = false; // whoops the ufo is already here, try again
1131 					}
1132 				}
1133 			}
1134 			// if ok, allocate it
1135 			if (placed)
1136 			{
1137 				for (int i = 0; i < craftMap->getSizeX() / 10; ++i)
1138 				{
1139 					for (int j = 0; j < craftMap->getSizeY() / 10; ++j)
1140 					{
1141 						landingzone[_craftX + i][_craftY + j] = true;
1142 						blocks[_craftX + i][_craftY + j] = _terrain->getRandomMapBlock(10, MT_LANDINGZONE);
1143 						blocksToDo--;
1144 					}
1145 				}
1146 			}
1147 		}
1148 	}
1149 
1150 	/* determine positioning of the urban terrain roads */
1151 	if (_save->getMissionType() == "STR_TERROR_MISSION")
1152 	{
1153 		int roadStyle = RNG::generate(0,99);
1154 		std::vector<int> roadChances = _terrain->getRoadTypeOdds();
1155 		bool EWRoad = roadStyle < roadChances.at(0);
1156 		bool NSRoad = !EWRoad && roadStyle < roadChances.at(0) + roadChances.at(1);
1157 		bool TwoRoads = !EWRoad && !NSRoad;
1158 		int roadX = _craftX;
1159 		int roadY = _craftY;
1160 		// make sure the road(s) are not crossing the craft landing site
1161 		while ((roadX >= _craftX && roadX < _craftX + (craftMap->getSizeX() / 10)) || (roadY >= _craftY && roadY < _craftY + (craftMap->getSizeY() / 10)))
1162 		{
1163 			roadX = RNG::generate(0, (_mapsize_x/10)- 1);
1164 			roadY = RNG::generate(0, (_mapsize_y/10)- 1);
1165 		}
1166 		if (TwoRoads)
1167 		{
1168 			// put a crossing on the X,Y position and fill the rest with roads
1169 			blocks[roadX][roadY] = _terrain->getRandomMapBlock(10, MT_CROSSING);
1170 			blocksToDo--;
1171 			EWRoad = true;
1172 			NSRoad = true;
1173 		}
1174 		if (EWRoad)
1175 		{
1176 			while (x < (_mapsize_x / 10))
1177 			{
1178 				if (blocks[x][roadY] == 0)
1179 				{
1180 					blocks[x][roadY] = _terrain->getRandomMapBlock(10, MT_EWROAD);
1181 					blocksToDo--;
1182 				}
1183 				x++;
1184 			}
1185 		}
1186 		if (NSRoad)
1187 		{
1188 			while (y < (_mapsize_y / 10))
1189 			{
1190 				if (blocks[roadX][y] == 0)
1191 				{
1192 					blocks[roadX][y] = _terrain->getRandomMapBlock(10, MT_NSROAD);
1193 					blocksToDo--;
1194 				}
1195 				y++;
1196 			}
1197 		}
1198 	}
1199 	/* determine positioning of base modules */
1200 	else if (_save->getMissionType() == "STR_BASE_DEFENSE")
1201 	{
1202 		for (std::vector<BaseFacility*>::const_iterator i = _base->getFacilities()->begin(); i != _base->getFacilities()->end(); ++i)
1203 		{
1204 			if ((*i)->getBuildTime() == 0)
1205 			{
1206 				int num = 0;
1207 				for (int y = (*i)->getY(); y < (*i)->getY() + (*i)->getRules()->getSize(); ++y)
1208 				{
1209 					for (int x = (*i)->getX(); x < (*i)->getX() + (*i)->getRules()->getSize(); ++x)
1210 					{
1211 						// lots of crazy stuff here, which is for the hangars (or other large base facilities one may create)
1212 						std::string mapname = (*i)->getRules()->getMapName();
1213 						std::ostringstream newname;
1214 						newname << mapname.substr(0, mapname.size()-2); // strip of last 2 digits
1215 						int mapnum = atoi(mapname.substr(mapname.size()-2, 2).c_str()); // get number
1216 						mapnum += num;
1217 						if (mapnum < 10) newname << 0;
1218 						newname << mapnum;
1219 						blocks[x][y] = _terrain->getMapBlock(newname.str());
1220 						storageBlocks[x][y] = ((*i)->getRules()->getStorage() > 0);
1221 						num++;
1222 					}
1223 				}
1224 			}
1225 		}
1226 
1227 		// fill with dirt
1228 		for (int i = 0; i < (_mapsize_x / 10); ++i)
1229 		{
1230 			for (int j = 0; j < (_mapsize_y / 10); ++j)
1231 			{
1232 				if (blocks[i][j] == 0)
1233 				{
1234 					blocks[i][j] = _terrain->getRandomMapBlock(10, MT_DIRT);
1235 				}
1236 			}
1237 		}
1238 
1239 		blocksToDo = 0;
1240 	}
1241 	/* determine positioning of base modules */
1242 	else if (_save->getMissionType() == "STR_ALIEN_BASE_ASSAULT" || _save->getMissionType() == "STR_MARS_THE_FINAL_ASSAULT")
1243 	{
1244 		int randX = RNG::generate(0, (_mapsize_x/10)- 2);
1245 		int randY = RNG::generate(0, (_mapsize_y/10)- 2);
1246 		// add the command center
1247 		blocks[randX][randY] = _terrain->getRandomMapBlock(20, (_save->getMissionType() == "STR_MARS_THE_FINAL_ASSAULT")?MT_FINALCOMM:MT_UBASECOMM);
1248 		blocksToDo--;
1249 		// mark mapblocks as used
1250 		blocks[randX + 1][randY] = dummy;
1251 		blocksToDo--;
1252 		blocks[randX + 1][randY + 1] = dummy;
1253 		blocksToDo--;
1254 		blocks[randX][randY + 1] = dummy;
1255 		blocksToDo--;
1256 		// add two lifts (not on top of the command center)
1257 		for (int i = 0; i < 2; i++)
1258 		{
1259 			while (blocks[randX][randY] != NULL)
1260 			{
1261 				randX = RNG::generate(0, (_mapsize_x/10)- 1);
1262 				randY = RNG::generate(0, (_mapsize_y/10)- 1);
1263 			}
1264 			// add the lift
1265 			blocks[randX][randY] = _terrain->getRandomMapBlock(10, MT_XCOMSPAWN);
1266 			blocksToDo--;
1267 		}
1268 	}
1269 	else if (_save->getMissionType() == "STR_MARS_CYDONIA_LANDING")
1270 	{
1271 		int randX = RNG::generate(0, (_mapsize_x/10)- 2);
1272 		int randY = RNG::generate(0, (_mapsize_y/10)- 2);
1273 		// add one lift
1274 		while (blocks[randX][randY] != NULL || landingzone[randX][randY])
1275 		{
1276 			randX = RNG::generate(0, (_mapsize_x/10)- 1);
1277 			randY = RNG::generate(0, (_mapsize_y/10)- 1);
1278 		}
1279 		// add the lift
1280 		blocks[randX][randY] = _terrain->getRandomMapBlock(10, MT_XCOMSPAWN);
1281 		blocksToDo--;
1282 	}
1283 
1284 	x = 0;
1285 	y = 0;
1286 	int maxLarge = _terrain->getLargeBlockLimit();
1287 	int curLarge = 0;
1288 	int tries = 0;
1289 	while (curLarge != maxLarge && tries <= 50)
1290 	{
1291 		int randX = RNG::generate(0, (_mapsize_x/10)- 2);
1292 		int randY = RNG::generate(0, (_mapsize_y/10)- 2);
1293 		if (!blocks[randX][randY] && !blocks[randX + 1][randY] && !blocks[randX + 1][randY + 1] && !blocks[randX][randY + 1]
1294 		&& !landingzone[randX][randY] && !landingzone[randX + 1][randY] && !landingzone[randX][randY + 1] && !landingzone[randX + 1][randY + 1])
1295 		{
1296 			blocks[randX][randY] = _terrain->getRandomMapBlock(20, MT_DEFAULT, true);
1297 			blocksToDo--;
1298 			// mark mapblocks as used
1299 			blocks[randX + 1][randY] = dummy;
1300 			blocksToDo--;
1301 			blocks[randX + 1][randY + 1] = dummy;
1302 			blocksToDo--;
1303 			blocks[randX][randY + 1] = dummy;
1304 			blocksToDo--;
1305 			curLarge++;
1306 		}
1307 		tries++;
1308 	}
1309 	/* Random map generation for crash/landing sites */
1310 	while (blocksToDo)
1311 	{
1312 		if (blocks[x][y] == 0)
1313 		{
1314 			if ((_save->getMissionType() == "STR_ALIEN_BASE_ASSAULT" || _save->getMissionType() == "STR_MARS_THE_FINAL_ASSAULT") && RNG::generate(0,100) > 60)
1315 			{
1316 				blocks[x][y] = _terrain->getRandomMapBlock(10, MT_CROSSING);
1317 			}
1318 			else
1319 			{
1320 				blocks[x][y] = _terrain->getRandomMapBlock(10, landingzone[x][y]?MT_LANDINGZONE:MT_DEFAULT);
1321 			}
1322 			blocksToDo--;
1323 			x++;
1324 		}
1325 		else
1326 		{
1327 			x++;
1328 		}
1329 
1330 		if (x >= (_mapsize_x / 10)) // reach the end
1331 		{
1332 			x = 0;
1333 			y++;
1334 		}
1335 	}
1336 
1337 	//reset the "times used" fields.
1338 	_terrain->resetMapBlocks();
1339 
1340 	for (std::vector<MapDataSet*>::iterator i = _terrain->getMapDataSets()->begin(); i != _terrain->getMapDataSets()->end(); ++i)
1341 	{
1342 		(*i)->loadData();
1343 		if (_game->getRuleset()->getMCDPatch((*i)->getName()))
1344 		{
1345 			_game->getRuleset()->getMCDPatch((*i)->getName())->modifyData(*i);
1346 		}
1347 		_save->getMapDataSets()->push_back(*i);
1348 		mapDataSetIDOffset++;
1349 	}
1350 
1351 	/* now load them up */
1352 	int segment = 0;
1353 	for (int itY = 0; itY < (_mapsize_y / 10); itY++)
1354 	{
1355 		for (int itX = 0; itX < (_mapsize_x / 10); itX++)
1356 		{
1357 			segments[itX][itY] = segment;
1358 			if (blocks[itX][itY] != 0 && blocks[itX][itY] != dummy)
1359 			{
1360 				loadMAP(blocks[itX][itY], itX * 10, itY * 10, _terrain, 0);
1361 				if (!landingzone[itX][itY])
1362 				{
1363 					loadRMP(blocks[itX][itY], itX * 10, itY * 10, segment++);
1364 				}
1365 			}
1366 		}
1367 	}
1368 
1369 	/* making passages between blocks in a base map */
1370 	if (_save->getMissionType() == "STR_BASE_DEFENSE" || _save->getMissionType() == "STR_ALIEN_BASE_ASSAULT" || _save->getMissionType() == "STR_MARS_THE_FINAL_ASSAULT")
1371 	{
1372 		int ewallfix = 14;
1373 		int swallfix = 13;
1374 		int ewallfixSet = 1;
1375 		int swallfixSet = 1;
1376 		if (_save->getMissionType() == "STR_ALIEN_BASE_ASSAULT" || _save->getMissionType() == "STR_MARS_THE_FINAL_ASSAULT")
1377 		{
1378 			ewallfix = 17; //  north wall
1379 			swallfix = 18; // west wall
1380 			ewallfixSet = 2;
1381 			swallfixSet = 2;
1382 		}
1383 
1384 		MapDataSet *mds = _terrain->getMapDataSets()->at(ewallfixSet);
1385 		MapBlock *dirt = _terrain->getRandomMapBlock(10, MT_DIRT);
1386 		for (int i = 0; i < (_mapsize_x / 10); ++i)
1387 		{
1388 			for (int j = 0; j < (_mapsize_y / 10); ++j)
1389 			{
1390 				if (blocks[i][j] == dirt)
1391 					continue;
1392 
1393 				// general stores - there is where the items are put
1394 				if (storageBlocks[i][j])
1395 				{
1396 					for (int k = i * 10; k != (i + 1) * 10; ++k)
1397 					{
1398 						for (int l = j * 10; l != (j + 1) * 10; ++l)
1399 						{
1400 							// we only want every other tile, giving us a "checkerboard" pattern
1401 							if ((k+l) % 2 == 0)
1402 							{
1403 								Tile *t = _save->getTile(Position(k,l,1));
1404 								Tile *tEast = _save->getTile(Position(k+1,l,1));
1405 								Tile *tSouth = _save->getTile(Position(k,l+1,1));
1406 								if (t && t->getMapData(MapData::O_FLOOR) && !t->getMapData(MapData::O_OBJECT) &&
1407 									tEast && !tEast->getMapData(MapData::O_WESTWALL) &&
1408 									tSouth && !tSouth->getMapData(MapData::O_NORTHWALL))
1409 								{
1410 									_save->getStorageSpace().push_back(Position(k, l, 1));
1411 								}
1412 							}
1413 						}
1414 					}
1415 					// let's put the inventory tile on the lower floor, just to be safe.
1416 					_craftInventoryTile = _save->getTile(Position((i*10)+5,(j*10)+5,0));
1417 				}
1418 
1419 				// drill east
1420 				if (i < (_mapsize_x / 10)-1
1421 					&& blocks[i+1][j] != dirt
1422 					&& _save->getTile(Position((i*10)+9,(j*10)+4,0))->getMapData(MapData::O_OBJECT)
1423 					&& (!_save->getTile(Position((i*10)+8,(j*10)+4,0))->getMapData(MapData::O_OBJECT)
1424 					|| (_save->getTile(Position((i*10)+9,(j*10)+4,0))->getMapData(MapData::O_OBJECT)
1425 					!= _save->getTile(Position((i*10)+8,(j*10)+4,0))->getMapData(MapData::O_OBJECT))))
1426 				{
1427 					// remove stuff
1428 					_save->getTile(Position((i*10)+9,(j*10)+3,0))->setMapData(0, -1, -1, MapData::O_WESTWALL);
1429 					_save->getTile(Position((i*10)+9,(j*10)+3,0))->setMapData(0, -1, -1, MapData::O_OBJECT);
1430 					_save->getTile(Position((i*10)+9,(j*10)+4,0))->setMapData(0, -1, -1, MapData::O_WESTWALL);
1431 					_save->getTile(Position((i*10)+9,(j*10)+4,0))->setMapData(0, -1, -1, MapData::O_OBJECT);
1432 					_save->getTile(Position((i*10)+9,(j*10)+5,0))->setMapData(0, -1, -1, MapData::O_WESTWALL);
1433 					_save->getTile(Position((i*10)+9,(j*10)+5,0))->setMapData(0, -1, -1, MapData::O_OBJECT);
1434 					if (_save->getTile(Position((i*10)+9,(j*10)+2,0))->getMapData(MapData::O_OBJECT))
1435 					{
1436 						//wallfix
1437 						_save->getTile(Position((i*10)+9,(j*10)+3,0))->setMapData(mds->getObjects()->at(ewallfix), ewallfix, ewallfixSet, MapData::O_NORTHWALL);
1438 						_save->getTile(Position((i*10)+9,(j*10)+6,0))->setMapData(mds->getObjects()->at(ewallfix), ewallfix, ewallfixSet, MapData::O_NORTHWALL);
1439 					}
1440 					if (_save->getMissionType() == "STR_ALIEN_BASE_ASSAULT" || _save->getMissionType() == "STR_MARS_THE_FINAL_ASSAULT")
1441 					{
1442 						//wallcornerfix
1443 						if (!_save->getTile(Position((i*10)+10,(j*10)+3,0))->getMapData(MapData::O_NORTHWALL))
1444 						{
1445 							_save->getTile(Position(((i+1)*10),(j*10)+3,0))->setMapData(mds->getObjects()->at(swallfix+1), swallfix+1, swallfixSet, MapData::O_OBJECT);
1446 						}
1447 						//floorfix
1448 						_save->getTile(Position((i*10)+9,(j*10)+3,0))->setMapData(_terrain->getMapDataSets()->at(1)->getObjects()->at(63), 63, 1, MapData::O_FLOOR);
1449 						_save->getTile(Position((i*10)+9,(j*10)+4,0))->setMapData(_terrain->getMapDataSets()->at(1)->getObjects()->at(63), 63, 1, MapData::O_FLOOR);
1450 						_save->getTile(Position((i*10)+9,(j*10)+5,0))->setMapData(_terrain->getMapDataSets()->at(1)->getObjects()->at(63), 63, 1, MapData::O_FLOOR);
1451 					}
1452 					// remove more stuff
1453 					_save->getTile(Position(((i+1)*10),(j*10)+3,0))->setMapData(0, -1, -1, MapData::O_WESTWALL);
1454 					_save->getTile(Position(((i+1)*10),(j*10)+4,0))->setMapData(0, -1, -1, MapData::O_WESTWALL);
1455 					_save->getTile(Position(((i+1)*10),(j*10)+5,0))->setMapData(0, -1, -1, MapData::O_WESTWALL);
1456 				}
1457 				// drill south
1458 				if (j < (_mapsize_y / 10)-1
1459 					&& blocks[i][j+1] != dirt
1460 					&& _save->getTile(Position((i*10)+4,(j*10)+9,0))->getMapData(MapData::O_OBJECT)
1461 					&& (!_save->getTile(Position((i*10)+4,(j*10)+8,0))->getMapData(MapData::O_OBJECT)
1462 					|| (_save->getTile(Position((i*10)+4,(j*10)+9,0))->getMapData(MapData::O_OBJECT)
1463 					!= _save->getTile(Position((i*10)+4,(j*10)+8,0))->getMapData(MapData::O_OBJECT))))
1464 				{
1465 					// remove stuff
1466 					_save->getTile(Position((i*10)+3,(j*10)+9,0))->setMapData(0, -1, -1, MapData::O_NORTHWALL);
1467 					_save->getTile(Position((i*10)+3,(j*10)+9,0))->setMapData(0, -1, -1, MapData::O_OBJECT);
1468 					_save->getTile(Position((i*10)+4,(j*10)+9,0))->setMapData(0, -1, -1, MapData::O_NORTHWALL);
1469 					_save->getTile(Position((i*10)+4,(j*10)+9,0))->setMapData(0, -1, -1, MapData::O_OBJECT);
1470 					_save->getTile(Position((i*10)+5,(j*10)+9,0))->setMapData(0, -1, -1, MapData::O_NORTHWALL);
1471 					_save->getTile(Position((i*10)+5,(j*10)+9,0))->setMapData(0, -1, -1, MapData::O_OBJECT);
1472 					if (_save->getTile(Position((i*10)+2,(j*10)+9,0))->getMapData(MapData::O_OBJECT))
1473 					{
1474 						// wallfix
1475 						_save->getTile(Position((i*10)+3,(j*10)+9,0))->setMapData(mds->getObjects()->at(swallfix), swallfix, swallfixSet, MapData::O_WESTWALL);
1476 						_save->getTile(Position((i*10)+6,(j*10)+9,0))->setMapData(mds->getObjects()->at(swallfix), swallfix, swallfixSet, MapData::O_WESTWALL);
1477 					}
1478 					if (_save->getMissionType() == "STR_ALIEN_BASE_ASSAULT" || _save->getMissionType() == "STR_MARS_THE_FINAL_ASSAULT")
1479 					{
1480 						// wallcornerfix
1481 						if (!_save->getTile(Position((i*10)+3,(j*10)+10,0))->getMapData(MapData::O_WESTWALL))
1482 						{
1483 							_save->getTile(Position((i*10)+3,((j+1)*10),0))->setMapData(mds->getObjects()->at(swallfix+1), swallfix+1, swallfixSet, MapData::O_OBJECT);
1484 						}
1485 						// floorfix
1486 						_save->getTile(Position((i*10)+3,(j*10)+9,0))->setMapData(_terrain->getMapDataSets()->at(1)->getObjects()->at(63), 63, 1, MapData::O_FLOOR);
1487 						_save->getTile(Position((i*10)+4,(j*10)+9,0))->setMapData(_terrain->getMapDataSets()->at(1)->getObjects()->at(63), 63, 1, MapData::O_FLOOR);
1488 						_save->getTile(Position((i*10)+5,(j*10)+9,0))->setMapData(_terrain->getMapDataSets()->at(1)->getObjects()->at(63), 63, 1, MapData::O_FLOOR);
1489 					}
1490 					// remove more stuff
1491 					_save->getTile(Position((i*10)+3,(j+1)*10,0))->setMapData(0, -1, -1, MapData::O_NORTHWALL);
1492 					_save->getTile(Position((i*10)+4,(j+1)*10,0))->setMapData(0, -1, -1, MapData::O_NORTHWALL);
1493 					_save->getTile(Position((i*10)+5,(j+1)*10,0))->setMapData(0, -1, -1, MapData::O_NORTHWALL);
1494 				}
1495 			}
1496 		}
1497 	}
1498 
1499 
1500 	if (_ufo != 0)
1501 	{
1502 		for (std::vector<MapDataSet*>::iterator i = _ufo->getRules()->getBattlescapeTerrainData()->getMapDataSets()->begin(); i != _ufo->getRules()->getBattlescapeTerrainData()->getMapDataSets()->end(); ++i)
1503 		{
1504 			(*i)->loadData();
1505 			if (_game->getRuleset()->getMCDPatch((*i)->getName()))
1506 			{
1507 				_game->getRuleset()->getMCDPatch((*i)->getName())->modifyData(*i);
1508 			}
1509 			_save->getMapDataSets()->push_back(*i);
1510 			craftDataSetIDOffset++;
1511 		}
1512 		loadMAP(ufoMap, ufoX * 10, ufoY * 10, _ufo->getRules()->getBattlescapeTerrainData(), mapDataSetIDOffset);
1513 		loadRMP(ufoMap, ufoX * 10, ufoY * 10, Node::UFOSEGMENT);
1514 		for (int i = 0; i < ufoMap->getSizeX() / 10; ++i)
1515 		{
1516 			for (int j = 0; j < ufoMap->getSizeY() / 10; j++)
1517 			{
1518 				segments[ufoX + i][ufoY + j] = Node::UFOSEGMENT;
1519 			}
1520 		}
1521 	}
1522 
1523 	if (_craft != 0 && (_save->getMissionType() != "STR_ALIEN_BASE_ASSAULT") && (_save->getMissionType() != "STR_MARS_THE_FINAL_ASSAULT"))
1524 	{
1525 		for (std::vector<MapDataSet*>::iterator i = _craft->getRules()->getBattlescapeTerrainData()->getMapDataSets()->begin(); i != _craft->getRules()->getBattlescapeTerrainData()->getMapDataSets()->end(); ++i)
1526 		{
1527 			(*i)->loadData();
1528 			if (_game->getRuleset()->getMCDPatch((*i)->getName()))
1529 			{
1530 				_game->getRuleset()->getMCDPatch((*i)->getName())->modifyData(*i);
1531 			}
1532 			_save->getMapDataSets()->push_back(*i);
1533 		}
1534 		loadMAP(craftMap, _craftX * 10, _craftY * 10, _craft->getRules()->getBattlescapeTerrainData(), mapDataSetIDOffset + craftDataSetIDOffset, true, true);
1535 		loadRMP(craftMap, _craftX * 10, _craftY * 10, Node::CRAFTSEGMENT);
1536 		for (int i = 0; i < craftMap->getSizeX() / 10; ++i)
1537 		{
1538 			for (int j = 0; j < craftMap->getSizeY() / 10; j++)
1539 			{
1540 				segments[_craftX + i][_craftY + j] = Node::CRAFTSEGMENT;
1541 			}
1542 		}
1543 	}
1544 
1545 	/* attach nodelinks to each other */
1546 	for (std::vector<Node*>::iterator i = _save->getNodes()->begin(); i != _save->getNodes()->end(); ++i)
1547 	{
1548 		Node *node = (*i);
1549 		int segmentX = node->getPosition().x / 10;
1550 		int segmentY = node->getPosition().y / 10;
1551 		int neighbourSegments[4];
1552 		int neighbourDirections[4] = { -2, -3, -4, -5 };
1553 		int neighbourDirectionsInverted[4] = { -4, -5, -2, -3 };
1554 
1555 		if (segmentX == (_mapsize_x / 10)-1)
1556 			neighbourSegments[0] = -1;
1557 		else
1558 			neighbourSegments[0] = segments[segmentX+1][segmentY];
1559 		if (segmentY == (_mapsize_y / 10)-1)
1560 			neighbourSegments[1] = -1;
1561 		else
1562 			neighbourSegments[1] = segments[segmentX][segmentY+1];
1563 		if (segmentX == 0)
1564 			neighbourSegments[2] = -1;
1565 		else
1566 			neighbourSegments[2] = segments[segmentX-1][segmentY];
1567 		if (segmentY == 0)
1568 			neighbourSegments[3] = -1;
1569 		else
1570 			neighbourSegments[3] = segments[segmentX][segmentY-1];
1571 
1572 		for (std::vector<int>::iterator j = node->getNodeLinks()->begin(); j != node->getNodeLinks()->end(); ++j )
1573 		{
1574 			for (int n = 0; n < 4; n++)
1575 			{
1576 				if (*j == neighbourDirections[n])
1577 				{
1578 					for (std::vector<Node*>::iterator k = _save->getNodes()->begin(); k != _save->getNodes()->end(); ++k)
1579 					{
1580 						if ((*k)->getSegment() == neighbourSegments[n])
1581 						{
1582 							for (std::vector<int>::iterator l = (*k)->getNodeLinks()->begin(); l != (*k)->getNodeLinks()->end(); ++l )
1583 							{
1584 								if (*l == neighbourDirectionsInverted[n])
1585 								{
1586 									*l = node->getID();
1587 									*j = (*k)->getID();
1588 								}
1589 							}
1590 						}
1591 					}
1592 				}
1593 			}
1594 		}
1595 	}
1596 
1597 	delete dummy;
1598 }
1599 
1600 
1601 /**
1602  * Loads an XCom format MAP file into the tiles of the battlegame.
1603  * @param mapblock Pointer to MapBlock.
1604  * @param xoff Mapblock offset in X direction.
1605  * @param yoff Mapblock offset in Y direction.
1606  * @param save Pointer to the current SavedBattleGame.
1607  * @param terrain Pointer to the Terrain rule.
1608  * @param discovered Whether or not this mapblock is discovered (eg. landingsite of the XCom plane).
1609  * @return int Height of the loaded mapblock (this is needed for spawpoint calculation...)
1610  * @sa http://www.ufopaedia.org/index.php?title=MAPS
1611  * @note Y-axis is in reverse order.
1612  */
loadMAP(MapBlock * mapblock,int xoff,int yoff,RuleTerrain * terrain,int mapDataSetOffset,bool discovered,bool craft)1613 int BattlescapeGenerator::loadMAP(MapBlock *mapblock, int xoff, int yoff, RuleTerrain *terrain, int mapDataSetOffset, bool discovered, bool craft)
1614 {
1615 	int sizex, sizey, sizez;
1616 	int x = xoff, y = yoff, z = 0;
1617 	char size[3];
1618 	unsigned char value[4];
1619 	std::ostringstream filename;
1620 	filename << "MAPS/" << mapblock->getName() << ".MAP";
1621 	int terrainObjectID;
1622 
1623 	// Load file
1624 	std::ifstream mapFile (CrossPlatform::getDataFile(filename.str()).c_str(), std::ios::in| std::ios::binary);
1625 	if (!mapFile)
1626 	{
1627 		throw Exception(filename.str() + " not found");
1628 	}
1629 
1630 	mapFile.read((char*)&size, sizeof(size));
1631 	sizey = (int)size[0];
1632 	sizex = (int)size[1];
1633 	sizez = (int)size[2];
1634 
1635 	if (sizez > _save->getMapSizeZ())
1636 	{
1637 		throw Exception("Height of map too big for this mission");
1638 	}
1639 
1640 	z += sizez - 1;
1641 	mapblock->setSizeZ(sizez);
1642 
1643 	for (int i = _mapsize_z-1; i >0; i--)
1644 	{
1645 		// check if there is already a layer - if so, we have to move Z up
1646 		MapData *floor = _save->getTile(Position(x, y, i))->getMapData(MapData::O_FLOOR);
1647 		if (floor != 0)
1648 		{
1649 			z += i;
1650 			if (craft)
1651 			{
1652 				_craftZ = i;
1653 			}
1654 			break;
1655 		}
1656 	}
1657 
1658 	if (z > (_save->getMapSizeZ()-1))
1659 	{
1660 		throw Exception("Something is wrong in your map definitions");
1661 	}
1662 
1663 	while (mapFile.read((char*)&value, sizeof(value)))
1664 	{
1665 		for (int part = 0; part < 4; part++)
1666 		{
1667 			terrainObjectID = (int)((unsigned char)value[part]);
1668 			if (terrainObjectID>0)
1669 			{
1670 				int mapDataSetID = mapDataSetOffset;
1671 				int mapDataID = terrainObjectID;
1672 				MapData *md = terrain->getMapData(&mapDataID, &mapDataSetID);
1673 				_save->getTile(Position(x, y, z))->setMapData(md, mapDataID, mapDataSetID, part);
1674 			}
1675 			// if the part is empty and it's not a floor, remove it
1676 			// it prevents growing grass in UFOs
1677 			if (terrainObjectID == 0 && part > 0)
1678 			{
1679 				_save->getTile(Position(x, y, z))->setMapData(0, -1, -1, part);
1680 			}
1681 		}
1682 		_save->getTile(Position(x, y, z))->setDiscovered(discovered, 2);
1683 
1684 		x++;
1685 
1686 		if (x == (sizex + xoff))
1687 		{
1688 			x = xoff;
1689 			y++;
1690 		}
1691 		if (y == (sizey + yoff))
1692 		{
1693 			y = yoff;
1694 			z--;
1695 		}
1696 	}
1697 
1698 	if (!mapFile.eof())
1699 	{
1700 		throw Exception("Invalid MAP file");
1701 	}
1702 
1703 	mapFile.close();
1704 
1705 	return sizez;
1706 }
1707 
1708 /**
1709  * Loads an XCom format RMP file into the spawnpoints of the battlegame.
1710  * @param mapblock Pointer to MapBlock.
1711  * @param xoff Mapblock offset in X direction.
1712  * @param yoff Mapblock offset in Y direction.
1713  * @param segment Mapblock segment.
1714  * @sa http://www.ufopaedia.org/index.php?title=ROUTES
1715  */
loadRMP(MapBlock * mapblock,int xoff,int yoff,int segment)1716 void BattlescapeGenerator::loadRMP(MapBlock *mapblock, int xoff, int yoff, int segment)
1717 {
1718 	int id = 0;
1719 	char value[24];
1720 	std::ostringstream filename;
1721 	filename << "ROUTES/" << mapblock->getName() << ".RMP";
1722 
1723 	// Load file
1724 	std::ifstream mapFile (CrossPlatform::getDataFile(filename.str()).c_str(), std::ios::in| std::ios::binary);
1725 	if (!mapFile)
1726 	{
1727 		throw Exception(filename.str() + " not found");
1728 	}
1729 
1730 	size_t nodeOffset = _save->getNodes()->size();
1731 
1732 	while (mapFile.read((char*)&value, sizeof(value)))
1733 	{
1734 		if( (int)value[0] < mapblock->getSizeY() && (int)value[1] < mapblock->getSizeX() && (int)value[2] < _mapsize_z )
1735 		{
1736 			Node *node = new Node(nodeOffset + id, Position(xoff + (int)value[1], yoff + (int)value[0], mapblock->getSizeZ() - 1 - (int)value[2]), segment, (int)value[19], (int)value[20], (int)value[21], (int)value[22], (int)value[23]);
1737 			for (int j=0;j<5;++j)
1738 			{
1739 				int connectID = (int)((signed char)value[4 + j*3]);
1740 				if (connectID > -1)
1741 				{
1742 					connectID += nodeOffset;
1743 				}
1744 				node->getNodeLinks()->push_back(connectID);
1745 			}
1746 			_save->getNodes()->push_back(node);
1747 		}
1748 		id++;
1749 	}
1750 
1751 	if (!mapFile.eof())
1752 	{
1753 		throw Exception("Invalid RMP file");
1754 	}
1755 
1756 	mapFile.close();
1757 }
1758 
1759 /**
1760  * Fill power sources with an elerium-115 object.
1761  */
fuelPowerSources()1762 void BattlescapeGenerator::fuelPowerSources()
1763 {
1764 	for (int i = 0; i < _save->getMapSizeXYZ(); ++i)
1765 	{
1766 		if (_save->getTiles()[i]->getMapData(MapData::O_OBJECT)
1767 			&& _save->getTiles()[i]->getMapData(MapData::O_OBJECT)->getSpecialType() == UFO_POWER_SOURCE)
1768 		{
1769 			BattleItem *elerium = new BattleItem(_game->getRuleset()->getItem("STR_ELERIUM_115"), _save->getCurrentItemId());
1770 			_save->getItems()->push_back(elerium);
1771 			_save->getTiles()[i]->addItem(elerium, _game->getRuleset()->getInventory("STR_GROUND"));
1772 		}
1773 	}
1774 }
1775 
1776 
1777 /**
1778  * When a UFO crashes, there is a 75% chance for each powersource to explode.
1779  */
explodePowerSources()1780 void BattlescapeGenerator::explodePowerSources()
1781 {
1782 	for (int i = 0; i < _save->getMapSizeXYZ(); ++i)
1783 	{
1784 		if (_save->getTiles()[i]->getMapData(MapData::O_OBJECT)
1785 			&& _save->getTiles()[i]->getMapData(MapData::O_OBJECT)->getSpecialType() == UFO_POWER_SOURCE && RNG::percent(75))
1786 		{
1787 			Position pos;
1788 			pos.x = _save->getTiles()[i]->getPosition().x*16;
1789 			pos.y = _save->getTiles()[i]->getPosition().y*16;
1790 			pos.z = (_save->getTiles()[i]->getPosition().z*24) +12;
1791 			_save->getTileEngine()->explode(pos, 180+RNG::generate(0,70), DT_HE, 10);
1792 		}
1793 	}
1794 	Tile *t = _save->getTileEngine()->checkForTerrainExplosions();
1795 	while (t)
1796 	{
1797 		Position p = Position(t->getPosition().x * 16, t->getPosition().y * 16, t->getPosition().z * 24);
1798 		p += Position(8,8,0);
1799 		_save->getTileEngine()->explode(p, t->getExplosive(), DT_HE, t->getExplosive() / 10);
1800 		t = _save->getTileEngine()->checkForTerrainExplosions();
1801 	}
1802 }
1803 
1804 /**
1805  * Spawns 1-16 civilians on a terror mission.
1806  * @param max Maximum number of civilians to spawn.
1807  */
deployCivilians(int max)1808 void BattlescapeGenerator::deployCivilians(int max)
1809 {
1810 	if (max)
1811 	{
1812 		// inevitably someone will point out that ufopaedia says 0-16 civilians.
1813 		// to that person:  i looked at the code and it says otherwise.
1814 		// 0 civilians would only be a possibility if there were already 80 units,
1815 		// or no spawn nodes for civilians.
1816 		int number = RNG::generate(max/2, max);
1817 
1818 		if (number > 0)
1819 		{
1820 			for (int i = 0; i < number; ++i)
1821 			{
1822 				size_t pick = RNG::generate(0, _terrain->getCivilianTypes().size() -1);
1823 				addCivilian(_game->getRuleset()->getUnit(_terrain->getCivilianTypes().at(pick)));
1824 			}
1825 		}
1826 	}
1827 }
1828 
1829 /**
1830  * Sets the alien base involved in the battle.
1831  * @param base Pointer to alien base.
1832  */
setAlienBase(AlienBase * base)1833 void BattlescapeGenerator::setAlienBase(AlienBase *base)
1834 {
1835 	_alienBase = base;
1836 	_alienBase->setInBattlescape(true);
1837 }
1838 
1839 /**
1840  * Places a unit near a friendly unit.
1841  * @param unit Pointer to the unit in question.
1842  * @return If we successfully placed the unit.
1843  */
placeUnitNearFriend(BattleUnit * unit)1844 bool BattlescapeGenerator::placeUnitNearFriend(BattleUnit *unit)
1845 {
1846 	Position entryPoint = Position(-1, -1, -1);
1847 	int tries = 100;
1848 	while (entryPoint == Position(-1, -1, -1) && tries)
1849 	{
1850 		BattleUnit* k = _save->getUnits()->at(RNG::generate(0, _save->getUnits()->size()-1));
1851 		if (k->getFaction() == unit->getFaction() && k->getPosition() != Position(-1, -1, -1) && k->getArmor()->getSize() == 1)
1852 		{
1853 			entryPoint = k->getPosition();
1854 		}
1855 		--tries;
1856 	}
1857 	if (tries && _save->placeUnitNearPosition(unit, entryPoint))
1858 	{
1859 		return true;
1860 	}
1861 	return false;
1862 }
1863 
1864 
1865 /**
1866  * Gets battlescape terrain using globe texture and latitude.
1867  * @param tex Globe texture.
1868  * @param lat Latitude.
1869  * @return Pointer to ruleterrain.
1870  */
getTerrain(int tex,double lat)1871 RuleTerrain *BattlescapeGenerator::getTerrain(int tex, double lat)
1872 {
1873 	RuleTerrain *t =  0;
1874 	const std::vector<std::string> &terrains = _game->getRuleset()->getTerrainList();
1875 	for (std::vector<std::string>::const_iterator i = terrains.begin(); i != terrains.end(); ++i)
1876 	{
1877 		t =  _game->getRuleset()->getTerrain(*i);
1878 		for (std::vector<int>::iterator j = t->getTextures()->begin(); j != t->getTextures()->end(); ++j )
1879 		{
1880 			if (*j == tex && (t->getHemisphere() == 0 || (t->getHemisphere() < 0 && lat < 0) || (t->getHemisphere() > 0 && lat >= 0)))
1881 			{
1882 				return t;
1883 			}
1884 		}
1885 	}
1886 
1887 	assert(0 && "No matching terrain for globe texture");
1888 	return t;
1889 }
1890 
1891 /**
1892 * Creates a mini-battle-save for managing inventory from the Geoscape.
1893 * Kids, don't try this at home!
1894 * @param craft Pointer to craft to manage.
1895 */
runInventory(Craft * craft)1896 void BattlescapeGenerator::runInventory(Craft *craft)
1897 {
1898 	// we need to fake a map for soldier placement
1899 	_baseInventory = true;
1900 	int soldiers = craft->getNumSoldiers();
1901 	_mapsize_x = soldiers;
1902 	_mapsize_y = 1;
1903 	_mapsize_z = 1;
1904 	_save->initMap(_mapsize_x, _mapsize_y, _mapsize_z);
1905 	MapDataSet *set = new MapDataSet("dummy");
1906 	MapData *data = new MapData(set);
1907 	for (int i = 0; i < soldiers; ++i)
1908 	{
1909 		Tile *tile = _save->getTiles()[i];
1910 		tile->setMapData(data, 0, 0, MapData::O_FLOOR);
1911 		tile->getMapData(MapData::O_FLOOR)->setSpecialType(START_POINT, 0);
1912 		tile->getMapData(MapData::O_FLOOR)->setTUWalk(0);
1913 		tile->getMapData(MapData::O_FLOOR)->setFlags(false, false, false, 0, false, false, false, false, false);
1914 	}
1915 
1916 	// ok now generate the battleitems for inventory
1917 	setCraft(craft);
1918 	deployXCOM();
1919 	delete data;
1920 	delete set;
1921 }
1922 
1923 /**
1924  * Loads all XCom weaponry before anything else is distributed.
1925  */
loadWeapons()1926 void BattlescapeGenerator::loadWeapons()
1927 {
1928 	// let's try to load this weapon, whether we equip it or not.
1929 	for (std::vector<BattleItem*>::iterator i = _craftInventoryTile->getInventory()->begin(); i != _craftInventoryTile->getInventory()->end(); ++i)
1930 	{
1931 		if (!(*i)->getRules()->isFixed() &&
1932 			!(*i)->getRules()->getCompatibleAmmo()->empty() &&
1933 			(*i)->getAmmoItem() == 0 &&
1934 			((*i)->getRules()->getBattleType() == BT_FIREARM || (*i)->getRules()->getBattleType() == BT_MELEE))
1935 		{
1936 			bool loaded = false;
1937 			for (std::vector<BattleItem*>::iterator j = _craftInventoryTile->getInventory()->begin(); j != _craftInventoryTile->getInventory()->end() && !loaded; ++j)
1938 			{
1939 				if ((*j)->getSlot() == _game->getRuleset()->getInventory("STR_GROUND") && (*i)->setAmmoItem((*j)) == 0)
1940 				{
1941 					_save->getItems()->push_back(*j);
1942 					(*j)->setXCOMProperty(true);
1943 					(*j)->setSlot(_game->getRuleset()->getInventory("STR_RIGHT_HAND"));
1944 					loaded = true;
1945 				}
1946 			}
1947 		}
1948 	}
1949 	for (std::vector<BattleItem*>::iterator i = _craftInventoryTile->getInventory()->begin(); i != _craftInventoryTile->getInventory()->end();)
1950 	{
1951 		if ((*i)->getSlot() != _game->getRuleset()->getInventory("STR_GROUND"))
1952 		{
1953 			i = _craftInventoryTile->getInventory()->erase(i);
1954 			continue;
1955 		}
1956 		++i;
1957 	}
1958 
1959 }
1960 }
1961