1 /***************************************************************************
2  *      Mechanized Assault and Exploration Reloaded Projectfile            *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19 
20 #include "game/data/map/map.h"
21 
22 #include "game/data/units/building.h"
23 #include "utility/listhelpers.h"
24 #include "utility/files.h"
25 #include "utility/log.h"
26 #include "game/data/player/player.h"
27 #include "settings.h"
28 #include "game/data/units/vehicle.h"
29 #include "video.h"
30 #include "utility/position.h"
31 #include "utility/random.h"
32 
33 #if 1 // TODO: [SDL2]: SDL_SetColors
SDL_SetColors(SDL_Surface * surface,SDL_Color * colors,int index,int size)34 inline void SDL_SetColors (SDL_Surface* surface, SDL_Color* colors, int index, int size)
35 {
36 	SDL_SetPaletteColors (surface->format->palette, colors, index, size);
37 }
38 #endif
39 
40 
sTerrain()41 sTerrain::sTerrain() :
42 	water (false),
43 	coast (false),
44 	blocked (false)
45 {}
46 
sTerrain(sTerrain && other)47 sTerrain::sTerrain(sTerrain&& other) :
48 	sf(std::move(other.sf)),
49 	sf_org(std::move(other.sf_org)),
50 	shw(std::move(other.shw)),
51 	shw_org(std::move(other.shw_org)),
52 	water(other.water),
53 	coast(other.coast),
54 	blocked(other.blocked)
55 {}
56 
operator =(sTerrain && other)57 sTerrain& sTerrain::operator=(sTerrain&& other)
58 {
59 	sf = std::move(other.sf);
60 	sf_org = std::move(other.sf_org);
61 	shw = std::move(other.shw);
62 	shw_org = std::move(other.shw_org);
63 	water = other.water;
64 	coast = other.coast;
65 	blocked = other.blocked;
66 
67 	return *this;
68 }
69 
cMapField()70 cMapField::cMapField()
71 {}
72 
getVehicle() const73 cVehicle* cMapField::getVehicle() const
74 {
75 	if (vehicles.empty()) return nullptr;
76 	return vehicles[0];
77 }
78 
getPlane() const79 cVehicle* cMapField::getPlane() const
80 {
81 	if (planes.empty()) return nullptr;
82 	return planes[0];
83 }
84 
getBuildings() const85 const std::vector<cBuilding*>& cMapField::getBuildings() const
86 {
87 	return buildings;
88 }
89 
getVehicles() const90 const std::vector<cVehicle*>& cMapField::getVehicles() const
91 {
92 	return vehicles;
93 }
94 
getPlanes() const95 const std::vector<cVehicle*>& cMapField::getPlanes() const
96 {
97 	return planes;
98 }
99 
getUnits(std::vector<cUnit * > & units) const100 void cMapField::getUnits (std::vector<cUnit*>& units) const
101 {
102 	units.clear();
103 	units.reserve (vehicles.size() + buildings.size() + planes.size());
104 	units.insert (units.end(), vehicles.begin(), vehicles.end());
105 	units.insert (units.end(), buildings.begin(), buildings.end());
106 	units.insert (units.end(), planes.begin(), planes.end());
107 }
108 
getBuilding() const109 cBuilding* cMapField::getBuilding() const
110 {
111 	if (buildings.empty()) return nullptr;
112 	return buildings[0];
113 }
114 
getTopBuilding() const115 cBuilding* cMapField::getTopBuilding() const
116 {
117 	if (buildings.empty()) return nullptr;
118 	cBuilding* building = buildings[0];
119 
120 	if ((building->data.surfacePosition == sUnitData::SURFACE_POS_GROUND ||
121 		 building->data.surfacePosition == sUnitData::SURFACE_POS_ABOVE) &&
122 		building->getOwner())
123 		return building;
124 	return nullptr;
125 }
126 
getBaseBuilding() const127 cBuilding* cMapField::getBaseBuilding() const
128 {
129 	for (cBuilding* building : buildings)
130 	{
131 		if (building->data.surfacePosition != sUnitData::SURFACE_POS_GROUND &&
132 			building->data.surfacePosition != sUnitData::SURFACE_POS_ABOVE &&
133 			building->getOwner())
134 		{
135 			return building;
136 		}
137 	}
138 	return nullptr;
139 }
140 
getRubble() const141 cBuilding* cMapField::getRubble() const
142 {
143 	for (cBuilding* building : buildings)
144 		if (!building->getOwner())
145 			return building;
146 	return nullptr;
147 }
148 
getMine() const149 cBuilding* cMapField::getMine() const
150 {
151 	for (cBuilding* building : buildings)
152 		if (building->data.explodesOnContact)
153 			return building;
154 	return nullptr;
155 }
156 
addBuilding(cBuilding & building,size_t index)157 void cMapField::addBuilding (cBuilding& building, size_t index)
158 {
159 	assert (index <= buildings.size());
160 	buildings.insert (buildings.begin() + index, &building);
161 
162 	buildingsChanged();
163 	unitsChanged();
164 }
addVehicle(cVehicle & vehicle,size_t index)165 void cMapField::addVehicle (cVehicle& vehicle, size_t index)
166 {
167 	assert (index <= vehicles.size());
168 	vehicles.insert (vehicles.begin() + index, &vehicle);
169 
170 	vehiclesChanged();
171 	unitsChanged();
172 }
173 
addPlane(cVehicle & plane,size_t index)174 void cMapField::addPlane (cVehicle& plane, size_t index)
175 {
176 	assert (index <= planes.size());
177 	planes.insert (planes.begin() + index, &plane);
178 
179 	planesChanged();
180 	unitsChanged();
181 }
182 
removeBuilding(const cBuilding & building)183 void cMapField::removeBuilding (const cBuilding& building)
184 {
185 	Remove (buildings, &building);
186 
187 	buildingsChanged();
188 	unitsChanged();
189 }
190 
removeVehicle(const cVehicle & vehicle)191 void cMapField::removeVehicle (const cVehicle& vehicle)
192 {
193 	Remove (vehicles, &vehicle);
194 
195 	vehiclesChanged();
196 	unitsChanged();
197 }
198 
removePlane(const cVehicle & plane)199 void cMapField::removePlane (const cVehicle& plane)
200 {
201 	Remove (planes, &plane);
202 
203 	planesChanged();
204 	unitsChanged();
205 }
206 
removeAll()207 void cMapField::removeAll()
208 {
209 	buildings.clear();
210 	vehicles.clear();
211 	planes.clear();
212 
213 	buildingsChanged();
214 	vehiclesChanged();
215 	planesChanged();
216 	unitsChanged();
217 }
218 
219 // cStaticMap //////////////////////////////////////////////////
220 
cStaticMap()221 cStaticMap::cStaticMap() : size (0), terrains()
222 {
223 }
224 
~cStaticMap()225 cStaticMap::~cStaticMap()
226 {
227 }
228 
getTerrain(const cPosition & position) const229 const sTerrain& cStaticMap::getTerrain (const cPosition& position) const
230 {
231 	return terrains[Kacheln[getOffset (position)]];
232 }
233 
isBlocked(const cPosition & position) const234 bool cStaticMap::isBlocked (const cPosition& position) const
235 {
236 	return getTerrain (position).blocked;
237 }
238 
isCoast(const cPosition & position) const239 bool cStaticMap::isCoast (const cPosition& position) const
240 {
241 	return getTerrain (position).coast;
242 }
243 
isWater(const cPosition & position) const244 bool cStaticMap::isWater (const cPosition& position) const
245 {
246 	return getTerrain (position).water;
247 }
248 
isValidPosition(const cPosition & position) const249 bool cStaticMap::isValidPosition (const cPosition& position) const
250 {
251 	return 0 <= position.x() && position.x() < size && 0 <= position.y() && position.y() < size;
252 }
253 
clear()254 void cStaticMap::clear()
255 {
256 	filename.clear();
257 	size = 0;
258 	terrains.clear();
259 	Kacheln.clear();
260 }
261 
isValid() const262 bool cStaticMap::isValid() const
263 {
264 	return !filename.empty();
265 }
266 
267 /** Loads a map file */
loadMap(const std::string & filename_)268 bool cStaticMap::loadMap (const std::string& filename_)
269 {
270 	clear();
271 	// Open File
272 	filename = filename_;
273 	Log.write ("Loading map \"" + filename_ + "\"", cLog::eLOG_TYPE_DEBUG);
274 
275 	// first try in the factory maps directory
276 	std::string fullFilename = cSettings::getInstance().getMapsPath() + PATH_DELIMITER + filename;
277 	SDL_RWops* fpMapFile = SDL_RWFromFile (fullFilename.c_str(), "rb");
278 	if (fpMapFile == nullptr)
279 	{
280 		// now try in the user's map directory
281 		std::string userMapsDir = getUserMapsDir();
282 		if (!userMapsDir.empty())
283 		{
284 			fullFilename = userMapsDir + filename;
285 			fpMapFile = SDL_RWFromFile (fullFilename.c_str(), "rb");
286 		}
287 	}
288 	if (fpMapFile == nullptr)
289 	{
290 		Log.write ("Cannot load map file: \"" + filename + "\"", cLog::eLOG_TYPE_WARNING);
291 		clear();
292 		return false;
293 	}
294 	char szFileTyp[4];
295 
296 	// check for typ
297 	SDL_RWread (fpMapFile, &szFileTyp, 1, 3);
298 	szFileTyp[3] = '\0';
299 	// WRL - interplays original mapformat
300 	// WRX - mapformat from russian mapeditor
301 	// DMO - for some reason some original maps have this filetype
302 	if (strcmp (szFileTyp, "WRL") != 0 && strcmp (szFileTyp, "WRX") != 0 && strcmp (szFileTyp, "DMO") != 0)
303 	{
304 		Log.write ("Wrong file format: \"" + filename + "\"", cLog::eLOG_TYPE_WARNING);
305 		SDL_RWclose (fpMapFile);
306 		clear();
307 		return false;
308 	}
309 	SDL_RWseek (fpMapFile, 2, SEEK_CUR);
310 
311 	// Read informations and get positions from the map-file
312 	const short sWidth = SDL_ReadLE16 (fpMapFile);
313 	Log.write ("SizeX: " + iToStr (sWidth), cLog::eLOG_TYPE_DEBUG);
314 	const short sHeight = SDL_ReadLE16 (fpMapFile);
315 	Log.write ("SizeY: " + iToStr (sHeight), cLog::eLOG_TYPE_DEBUG);
316 	SDL_RWseek (fpMapFile, sWidth * sHeight, SEEK_CUR); // Ignore Mini-Map
317 	const int iDataPos = SDL_RWtell (fpMapFile); // Map-Data
318 	SDL_RWseek (fpMapFile, sWidth * sHeight * 2, SEEK_CUR);
319 	const int iNumberOfTerrains = SDL_ReadLE16 (fpMapFile); // Read PicCount
320 	Log.write ("Number of terrains: " + iToStr (iNumberOfTerrains), cLog::eLOG_TYPE_DEBUG);
321 	const int iGraphicsPos = SDL_RWtell (fpMapFile); // Terrain Graphics
322 	const int iPalettePos = iGraphicsPos + iNumberOfTerrains * 64 * 64; // Palette
323 	const int iInfoPos = iPalettePos + 256 * 3; // Special informations
324 
325 	if (sWidth != sHeight)
326 	{
327 		Log.write ("Map must be quadratic!: \"" + filename + "\"", cLog::eLOG_TYPE_WARNING);
328 		SDL_RWclose (fpMapFile);
329 		clear();
330 		return false;
331 	}
332 
333 	// Generate new Map
334 	this->size = std::max<int> (16, sWidth);
335 	Kacheln.resize (size * size, 0);
336 	terrains.resize(iNumberOfTerrains);
337 
338 	// Load Color Palette
339 	SDL_RWseek (fpMapFile, iPalettePos, SEEK_SET);
340 	for (int i = 0; i < 256; i++)
341 	{
342 		SDL_RWread (fpMapFile, palette + i, 3, 1);
343 	}
344 	//generate palette for terrains with fog
345 	for (int i = 0; i < 256; i++)
346 	{
347 		palette_shw[i].r = (unsigned char) (palette[i].r * 0.6f);
348 		palette_shw[i].g = (unsigned char) (palette[i].g * 0.6f);
349 		palette_shw[i].b = (unsigned char) (palette[i].b * 0.6f);
350 		palette[i].a = 255;
351 		palette_shw[i].a = 255;
352 	}
353 
354 	// Load necessary Terrain Graphics
355 	for (int iNum = 0; iNum < iNumberOfTerrains; iNum++)
356 	{
357 		unsigned char cByte; // one Byte
358 		// load terrain type info
359 		SDL_RWseek (fpMapFile, iInfoPos + iNum, SEEK_SET);
360 		SDL_RWread (fpMapFile, &cByte, 1, 1);
361 
362 		switch (cByte)
363 		{
364 			case 0:
365 				//normal terrain without special property
366 				break;
367 			case 1:
368 				terrains[iNum].water = true;
369 				break;
370 			case 2:
371 				terrains[iNum].coast = true;
372 				break;
373 			case 3:
374 				terrains[iNum].blocked = true;
375 				break;
376 			default:
377 				Log.write ("unknown terrain type " + iToStr (cByte) + " on tile " + iToStr (iNum) + " found. Handled as blocked!", cLog::eLOG_TYPE_WARNING);
378 				terrains[iNum].blocked = true;
379 				//SDL_RWclose (fpMapFile);
380 				//return false;
381 		}
382 
383 		//load terrain graphic
384 		AutoSurface surface (cStaticMap::loadTerrGraph (fpMapFile, iGraphicsPos, palette, iNum));
385 		if (surface == nullptr)
386 		{
387 			Log.write ("EOF while loading terrain number " + iToStr (iNum), cLog::eLOG_TYPE_WARNING);
388 			SDL_RWclose (fpMapFile);
389 			clear();
390 			return false;
391 		}
392 		copySrfToTerData (*surface, iNum);
393 	}
394 
395 	// Load map data
396 	SDL_RWseek (fpMapFile, iDataPos, SEEK_SET);
397 	for (int iY = 0; iY < size; iY++)
398 	{
399 		for (int iX = 0; iX < size; iX++)
400 		{
401 			int Kachel = SDL_ReadLE16 (fpMapFile);
402 			if (Kachel >= iNumberOfTerrains)
403 			{
404 				Log.write ("a map field referred to a nonexisting terrain: " + iToStr (Kachel), cLog::eLOG_TYPE_WARNING);
405 				SDL_RWclose (fpMapFile);
406 				clear();
407 				return false;
408 			}
409 			Kacheln[iY * size + iX] = Kachel;
410 		}
411 	}
412 	SDL_RWclose (fpMapFile);
413 	return true;
414 }
415 
loadTerrGraph(SDL_RWops * fpMapFile,int iGraphicsPos,const SDL_Color (& colors)[256],int iNum)416 /*static*/AutoSurface cStaticMap::loadTerrGraph (SDL_RWops* fpMapFile, int iGraphicsPos, const SDL_Color (&colors)[256], int iNum)
417 {
418 	// Create new surface and copy palette
419 	AutoSurface surface (SDL_CreateRGBSurface (0, 64, 64, 8, 0, 0, 0, 0));
420 	surface->pitch = surface->w;
421 
422 	SDL_SetPaletteColors (surface->format->palette, colors, 0, 256);
423 
424 	// Go to position of filedata
425 	SDL_RWseek (fpMapFile, iGraphicsPos + 64 * 64 * (iNum), SEEK_SET);
426 
427 	// Read pixel data and write to surface
428 	if (SDL_RWread (fpMapFile, surface->pixels, 1, 64 * 64) != 64 * 64) return 0;
429 	return surface;
430 }
431 
loadMapPreview(const std::string & mapName,int * mapSize)432 /*static*/AutoSurface cStaticMap::loadMapPreview (const std::string& mapName, int* mapSize)
433 {
434 	std::string mapPath = cSettings::getInstance().getMapsPath() + PATH_DELIMITER + mapName;
435 	// if no factory map of that name exists, try the custom user maps
436 
437 	SDL_RWops* mapFile = SDL_RWFromFile (mapPath.c_str(), "rb");
438 	if (mapFile == nullptr && !getUserMapsDir().empty())
439 	{
440 		mapPath = getUserMapsDir() + mapName;
441 		mapFile = SDL_RWFromFile (mapPath.c_str(), "rb");
442 	}
443 
444 	if (mapFile == nullptr) return nullptr;
445 
446 	SDL_RWseek (mapFile, 5, SEEK_SET);
447 	int size = SDL_ReadLE16 (mapFile);
448 	struct { unsigned char cBlue, cGreen, cRed; } Palette[256];
449 	short sGraphCount;
450 	SDL_RWseek (mapFile, 2 + size * size * 3, SEEK_CUR);
451 	sGraphCount = SDL_ReadLE16 (mapFile);
452 	SDL_RWseek (mapFile, 64 * 64 * sGraphCount, SEEK_CUR);
453 	SDL_RWread (mapFile, &Palette, 3, 256);
454 
455 	AutoSurface mapSurface (SDL_CreateRGBSurface (0, size, size, 8, 0, 0, 0, 0));
456 	mapSurface->pitch = mapSurface->w;
457 
458 	mapSurface->format->palette->ncolors = 256;
459 	for (int j = 0; j < 256; j++)
460 	{
461 		mapSurface->format->palette->colors[j].r = Palette[j].cBlue;
462 		mapSurface->format->palette->colors[j].g = Palette[j].cGreen;
463 		mapSurface->format->palette->colors[j].b = Palette[j].cRed;
464 	}
465 	SDL_RWseek (mapFile, 9, SEEK_SET);
466 	const int byteReadCount = SDL_RWread (mapFile, mapSurface->pixels, 1, size * size);
467 	SDL_RWclose (mapFile);
468 
469 	if (byteReadCount != size * size)
470 	{
471 		// error.
472 		return nullptr;
473 	}
474 	const int MAPWINSIZE = 112;
475 	if (mapSurface->w != MAPWINSIZE || mapSurface->h != MAPWINSIZE) // resize map
476 	{
477 		mapSurface = AutoSurface (scaleSurface (mapSurface.get(), nullptr, MAPWINSIZE, MAPWINSIZE));
478 	}
479 
480 	if (mapSize != nullptr) *mapSize = size;
481 	return mapSurface;
482 }
483 
copySrfToTerData(SDL_Surface & surface,int iNum)484 void cStaticMap::copySrfToTerData (SDL_Surface& surface, int iNum)
485 {
486 	//before the surfaces are copied, the colortable of both surfaces has to be equal
487 	//This is needed to make sure, that the pixeldata is copied 1:1
488 
489 	//copy the normal terrains
490 	terrains[iNum].sf_org = AutoSurface (SDL_CreateRGBSurface (0, 64, 64, 8, 0, 0, 0, 0));
491 	SDL_SetPaletteColors (terrains[iNum].sf_org->format->palette, surface.format->palette->colors, 0, 256);
492 	SDL_BlitSurface (&surface, nullptr, terrains[iNum].sf_org.get(), nullptr);
493 
494 	terrains[iNum].sf = AutoSurface (SDL_CreateRGBSurface (0, 64, 64, 8, 0, 0, 0, 0));
495 	SDL_SetPaletteColors (terrains[iNum].sf->format->palette, surface.format->palette->colors, 0, 256);
496 	SDL_BlitSurface (&surface, nullptr, terrains[iNum].sf.get(), nullptr);
497 
498 	//copy the terrains with fog
499 	terrains[iNum].shw_org = AutoSurface (SDL_CreateRGBSurface (0, 64, 64, 8, 0, 0, 0, 0));
500 	SDL_SetColors (terrains[iNum].shw_org.get(), surface.format->palette->colors, 0, 256);
501 	SDL_BlitSurface (&surface, nullptr, terrains[iNum].shw_org.get(), nullptr);
502 
503 	terrains[iNum].shw = AutoSurface (SDL_CreateRGBSurface (0, 64, 64, 8, 0, 0, 0, 0));
504 	SDL_SetColors (terrains[iNum].shw.get(), surface.format->palette->colors, 0, 256);
505 	SDL_BlitSurface (&surface, nullptr, terrains[iNum].shw.get(), nullptr);
506 
507 	//now set the palette for the fog terrains
508 	SDL_SetColors (terrains[iNum].shw_org.get(), palette_shw, 0, 256);
509 	SDL_SetColors (terrains[iNum].shw.get(), palette_shw, 0, 256);
510 }
511 
scaleSurfaces(int pixelSize)512 void cStaticMap::scaleSurfaces (int pixelSize)
513 {
514 	for (sTerrain& t : terrains)
515 	{
516 		scaleSurface (t.sf_org.get(), t.sf.get(), pixelSize, pixelSize);
517 		scaleSurface (t.shw_org.get(), t.shw.get(), pixelSize, pixelSize);
518 	}
519 }
520 
generateNextAnimationFrame()521 void cStaticMap::generateNextAnimationFrame()
522 {
523 	//change palettes to display next frame
524 	SDL_Color temp = palette[127];
525 	memmove (palette + 97, palette + 96, 32 * sizeof (SDL_Color));
526 	palette[96]  = palette[103];
527 	palette[103] = palette[110];
528 	palette[110] = palette[117];
529 	palette[117] = palette[123];
530 	palette[123] = temp;
531 
532 	temp = palette_shw[127];
533 	memmove (palette_shw + 97, palette_shw + 96, 32 * sizeof (SDL_Color));
534 	palette_shw[96]  = palette_shw[103];
535 	palette_shw[103] = palette_shw[110];
536 	palette_shw[110] = palette_shw[117];
537 	palette_shw[117] = palette_shw[123];
538 	palette_shw[123] = temp;
539 
540 	//set the new palette for all terrain surfaces
541 	for (sTerrain& terrain : terrains)
542 	{
543 		SDL_SetColors (terrain.sf.get(), palette + 96, 96, 127);
544 		//SDL_SetColors (TerrainInUse[i]->sf_org, palette + 96, 96, 127);
545 		SDL_SetColors (terrain.shw.get(), palette_shw + 96, 96, 127);
546 		//SDL_SetColors (TerrainInUse[i]->shw_org, palette_shw + 96, 96, 127);
547 	}
548 }
549 
createBigSurface(int sizex,int sizey) const550 AutoSurface cStaticMap::createBigSurface (int sizex, int sizey) const
551 {
552 	AutoSurface mapSurface (SDL_CreateRGBSurface (0, sizex, sizey, Video.getColDepth(), 0, 0, 0, 0));
553 
554 	if (SDL_MUSTLOCK (mapSurface.get())) SDL_LockSurface (mapSurface.get());
555 	for (int x = 0; x < mapSurface->w; ++x)
556 	{
557 		const int terrainx = std::min ((x * size) / mapSurface->w, size - 1);
558 		const int offsetx = ((x * size) % mapSurface->w) * 64 / mapSurface->w;
559 
560 		for (int y = 0; y < mapSurface->h; y++)
561 		{
562 			const int terrainy = std::min ((y * size) / mapSurface->h, size - 1);
563 			const int offsety = ((y * size) % mapSurface->h) * 64 / mapSurface->h;
564 
565 			const sTerrain& t = this->getTerrain (cPosition (terrainx, terrainy));
566 			unsigned int ColorNr = * (static_cast<const unsigned char*> (t.sf_org->pixels) + (offsetx + offsety * 64));
567 
568 			unsigned char* pixel = reinterpret_cast<unsigned char*> (&static_cast<Uint32*> (mapSurface->pixels) [x + y * mapSurface->w]);
569 			pixel[0] = palette[ColorNr].b;
570 			pixel[1] = palette[ColorNr].g;
571 			pixel[2] = palette[ColorNr].r;
572 		}
573 	}
574 	if (SDL_MUSTLOCK (mapSurface.get())) SDL_UnlockSurface (mapSurface.get());
575 	return mapSurface;
576 }
577 
578 // Funktionen der Map-Klasse /////////////////////////////////////////////////
cMap(std::shared_ptr<cStaticMap> staticMap_)579 cMap::cMap (std::shared_ptr<cStaticMap> staticMap_) :
580 	staticMap (std::move (staticMap_))
581 {
582 	const int size = staticMap->getSize().x() * staticMap->getSize().y();
583 	fields = new cMapField[size];
584 	Resources.resize (size);
585 }
586 
~cMap()587 cMap::~cMap()
588 {
589 	delete [] fields;
590 }
591 
operator [](unsigned int offset) const592 cMapField& cMap::operator[] (unsigned int offset) const
593 {
594 	return fields[offset];
595 }
596 
getField(const cPosition & position)597 cMapField& cMap::getField (const cPosition& position)
598 {
599 	return fields[getOffset (position)];
600 }
601 
getField(const cPosition & position) const602 const cMapField& cMap::getField (const cPosition& position) const
603 {
604 	return fields[getOffset (position)];
605 }
606 
isWaterOrCoast(const cPosition & position) const607 bool cMap::isWaterOrCoast (const cPosition& position) const
608 {
609 	const sTerrain& terrainType = staticMap->getTerrain (position);
610 	return terrainType.water | terrainType.coast;
611 }
612 
assignRessources(const cMap & rhs)613 void cMap::assignRessources (const cMap& rhs)
614 {
615 	Resources = rhs.Resources;
616 }
617 
618 //--------------------------------------------------------------------------
getHexValue(unsigned char byte)619 static std::string getHexValue (unsigned char byte)
620 {
621 	std::string str = "";
622 	const char hexChars[] = "0123456789ABCDEF";
623 	const unsigned char high = (byte >> 4) & 0x0F;
624 	const unsigned char low = byte & 0x0F;
625 
626 	str += hexChars[high];
627 	str += hexChars[low];
628 	return str;
629 }
630 
631 //--------------------------------------------------------------------------
resourcesToString() const632 std::string cMap::resourcesToString() const
633 {
634 	std::string str;
635 	str.reserve (4 * Resources.size() + 1);
636 	for (size_t i = 0; i != Resources.size(); ++i)
637 	{
638 		str += getHexValue (Resources[i].typ);
639 		str += getHexValue (Resources[i].value);
640 	}
641 	return str;
642 }
643 
644 //--------------------------------------------------------------------------
getByteValue(const std::string & str,int index)645 static unsigned char getByteValue (const std::string& str, int index)
646 {
647 	unsigned char first = str[index + 0] - '0';
648 	unsigned char second = str[index + 1] - '0';
649 
650 	if (first >= 'A' - '0') first -= 'A' - '0' - 10;
651 	if (second >= 'A' - '0') second -= 'A' - '0' - 10;
652 	return (first * 16 + second);
653 }
654 
655 //--------------------------------------------------------------------------
setResourcesFromString(const std::string & str)656 void cMap::setResourcesFromString (const std::string& str)
657 {
658 	for (size_t i = 0; i != Resources.size(); ++i)
659 	{
660 		Resources[i].typ = getByteValue (str, 4 * i);
661 		Resources[i].value = getByteValue (str, 4 * i + 2);
662 	}
663 }
664 
placeRessources(const std::vector<cPosition> & landingPositions,const cGameSettings & gameSetting)665 void cMap::placeRessources (const std::vector<cPosition>& landingPositions, const cGameSettings& gameSetting)
666 {
667 	std::fill (Resources.begin(), Resources.end(), sResources());
668 
669 	std::vector<int> resSpotTypes(landingPositions.size(), RES_METAL);
670 	std::vector<T_2<int>> resSpots;
671 	for (const auto& position : landingPositions)
672 	{
673 		resSpots.push_back(T_2<int> ((position.x() & ~1) + (RES_METAL % 2), (position.y() & ~1) + ((RES_METAL / 2) % 2)));
674 	}
675 
676 	const eGameSettingsResourceDensity density = gameSetting.getResourceDensity();
677 	eGameSettingsResourceAmount frequencies[RES_COUNT];
678 
679 	frequencies[RES_METAL] = gameSetting.getMetalAmount();
680 	frequencies[RES_OIL] = gameSetting.getOilAmount();
681 	frequencies[RES_GOLD] = gameSetting.getGoldAmount();
682 
683 	const std::size_t resSpotCount = (std::size_t) (getSize().x() * getSize().y() * 0.003f * (1.5f + getResourceDensityFactor (density)));
684 	const std::size_t playerCount = landingPositions.size();
685 	// create remaining resource positions
686 	while (resSpots.size() < resSpotCount)
687 	{
688 		T_2<int> pos;
689 
690 		pos.x = 2 + random (getSize().x() - 4);
691 		pos.y = 2 + random (getSize().y() - 4);
692 		resSpots.push_back(pos);
693 	}
694 	resSpotTypes.resize (resSpotCount);
695 	// Resourcen gleichmässiger verteilen
696 	for (std::size_t j = 0; j < 3; j++)
697 	{
698 		for (std::size_t i = playerCount; i < resSpotCount; i++)
699 		{
700 			T_2<float> d;
701 			for (std::size_t j = 0; j < resSpotCount; j++)
702 			{
703 				if (i == j) continue;
704 
705 				int diffx1 = resSpots[i].x - resSpots[j].x;
706 				int diffx2 = diffx1 + (getSize().x() - 4);
707 				int diffx3 = diffx1 - (getSize().x() - 4);
708 				int diffy1 = resSpots[i].y - resSpots[j].y;
709 				int diffy2 = diffy1 + (getSize().y() - 4);
710 				int diffy3 = diffy1 - (getSize().y() - 4);
711 				if (abs (diffx2) < abs (diffx1)) diffx1 = diffx2;
712 				if (abs (diffx3) < abs (diffx1)) diffx1 = diffx3;
713 				if (abs (diffy2) < abs (diffy1)) diffy1 = diffy2;
714 				if (abs (diffy3) < abs (diffy1)) diffy1 = diffy3;
715 				T_2<float> diff (diffx1, diffy1);
716 				if (diff == T_2<float>::Zero)
717 				{
718 					diff.x += 1;
719 				}
720 				const float dist = diff.dist();
721 				d += diff * (10.f / (dist * dist));
722 
723 			}
724 			resSpots[i] += T_2<int> (Round (d.x), Round (d.y));
725 			if (resSpots[i].x < 2) resSpots[i].x += getSize().x() - 4;
726 			if (resSpots[i].y < 2) resSpots[i].y += getSize().y() - 4;
727 			if (resSpots[i].x > getSize().x() - 3) resSpots[i].x -= getSize().x() - 4;
728 			if (resSpots[i].y > getSize().y() - 3) resSpots[i].y -= getSize().y() - 4;
729 
730 		}
731 	}
732 	// Resourcen Typ bestimmen
733 	for (std::size_t i = playerCount; i < resSpotCount; i++)
734 	{
735 		float amount[RES_COUNT] = {0.f, 0.f, 0.f, 0.f};
736 		for (std::size_t j = 0; j < i; j++)
737 		{
738 			const float maxDist = 40.f;
739 			float dist = sqrtf (resSpots[i].distSqr (resSpots[j]));
740 			if (dist < maxDist) amount[resSpotTypes[j]] += 1 - sqrtf (dist / maxDist);
741 		}
742 
743 		amount[RES_METAL] /= 1.0f;
744 		amount[RES_OIL] /= 0.8f;
745 		amount[RES_GOLD] /= 0.4f;
746 
747 		int type = RES_METAL;
748 		if (amount[RES_OIL] < amount[type]) type = RES_OIL;
749 		if (amount[RES_GOLD] < amount[type]) type = RES_GOLD;
750 
751 		resSpots[i].x &= ~1;
752 		resSpots[i].y &= ~1;
753 		resSpots[i].x += type % 2;
754 		resSpots[i].y += (type / 2) % 2;
755 
756 		resSpotTypes[i] = ((resSpots[i].y % 2) * 2) + (resSpots[i].x % 2);
757 	}
758 	// Resourcen platzieren
759 	for (std::size_t i = 0; i < resSpotCount; i++)
760 	{
761 		T_2<int> pos = resSpots[i];
762 		T_2<int> p;
763 		bool hasGold = random (100) < 40;
764 		const int minx = std::max (pos.x - 1, 0);
765 		const int maxx = std::min (pos.x + 1, getSize().x() - 1);
766 		const int miny = std::max (pos.y - 1, 0);
767 		const int maxy = std::min (pos.y + 1, getSize().y() - 1);
768 		for (p.y = miny; p.y <= maxy; ++p.y)
769 		{
770 			for (p.x = minx; p.x <= maxx; ++p.x)
771 			{
772 				T_2<int> absPos = p;
773 				int type = (absPos.y % 2) * 2 + (absPos.x % 2);
774 
775 				int index = getOffset (cPosition (absPos.x, absPos.y));
776 				if (type != RES_NONE &&
777 					((hasGold && i >= playerCount) || resSpotTypes[i] == RES_GOLD || type != RES_GOLD) &&
778 					!isBlocked (cPosition (absPos.x, absPos.y)))
779 				{
780 					Resources[index].typ = type;
781 					if (i >= playerCount)
782 					{
783 						Resources[index].value = 1 + random (2 + getResourceAmountFactor (frequencies[type]) * 2);
784 						if (p == pos) Resources[index].value += 3 + random (4 + getResourceAmountFactor (frequencies[type]) * 2);
785 					}
786 					else
787 					{
788 						Resources[index].value = 1 + 4 + getResourceAmountFactor (frequencies[type]);
789 						if (p == pos) Resources[index].value += 3 + 2 + getResourceAmountFactor (frequencies[type]);
790 					}
791 
792 					Resources[index].value = std::min<unsigned char> (16, Resources[index].value);
793 				}
794 			}
795 		}
796 	}
797 }
798 
getMapLevel(const cBuilding & building)799 /* static */ int cMap::getMapLevel (const cBuilding& building)
800 {
801 	const sUnitData& data = building.data;
802 
803 	if (data.surfacePosition == sUnitData::SURFACE_POS_BENEATH_SEA) return 9; // seamine
804 	if (data.surfacePosition == sUnitData::SURFACE_POS_ABOVE_SEA) return 7; // bridge
805 	if (data.surfacePosition == sUnitData::SURFACE_POS_BASE && data.canBeOverbuild) return 6; // platform
806 	if (data.surfacePosition == sUnitData::SURFACE_POS_BASE) return 5; // road
807 	if (!building.getOwner()) return 4;  // rubble
808 	if (data.surfacePosition == sUnitData::SURFACE_POS_ABOVE_BASE) return 3; // landmine
809 
810 	return 1; // other buildings
811 }
812 
getMapLevel(const cVehicle & vehicle)813 /* static */ int cMap::getMapLevel (const cVehicle& vehicle)
814 {
815 	if (vehicle.data.factorSea > 0 && vehicle.data.factorGround == 0) return 8; // ships
816 	if (vehicle.data.factorAir > 0) return 0; // planes
817 
818 	return 2; // other vehicles
819 }
820 
addBuilding(cBuilding & building,const cPosition & position)821 void cMap::addBuilding (cBuilding& building, const cPosition& position)
822 {
823 	//big base building are not implemented
824 	if (building.data.surfacePosition != sUnitData::SURFACE_POS_GROUND && building.data.isBig && building.getOwner()) return;
825 
826 	const int mapLevel = cMap::getMapLevel (building);
827 	size_t i = 0;
828 
829 	if (building.data.isBig)
830 	{
831 		auto& field = getField (position);
832 		i = 0;
833 		while (i < field.getBuildings().size() && cMap::getMapLevel (*field.getBuildings()[i]) < mapLevel) i++;
834 		field.addBuilding (building, i);
835 
836 		auto& fieldEast = getField (position + cPosition (1, 0));
837 		i = 0;
838 		while (i < fieldEast.getBuildings().size() && cMap::getMapLevel (*fieldEast.getBuildings()[i]) < mapLevel) i++;
839 		fieldEast.addBuilding (building, i);
840 
841 		auto& fieldSouth = getField (position + cPosition (0, 1));
842 		i = 0;
843 		while (i < fieldSouth.getBuildings().size() && cMap::getMapLevel (*fieldSouth.getBuildings()[i]) < mapLevel) i++;
844 		fieldSouth.addBuilding (building, i);
845 
846 		auto& fieldSouthEast = getField (position + cPosition (1, 1));
847 		i = 0;
848 		while (i < fieldSouthEast.getBuildings().size() && cMap::getMapLevel (*fieldSouthEast.getBuildings()[i]) < mapLevel) i++;
849 		fieldSouthEast.addBuilding (building, i);
850 	}
851 	else
852 	{
853 		auto& field = getField (position);
854 
855 		while (i < field.getBuildings().size() && cMap::getMapLevel (*field.getBuildings()[i]) < mapLevel) i++;
856 		field.addBuilding (building, i);
857 	}
858 	addedUnit (building);
859 }
860 
addVehicle(cVehicle & vehicle,const cPosition & position)861 void cMap::addVehicle (cVehicle& vehicle, const cPosition& position)
862 {
863 	auto& field = getField (position);
864 	if (vehicle.data.factorAir > 0)
865 	{
866 		field.addPlane (vehicle, 0);
867 	}
868 	else
869 	{
870 		field.addVehicle (vehicle, 0);
871 	}
872 	addedUnit (vehicle);
873 }
874 
deleteBuilding(const cBuilding & building)875 void cMap::deleteBuilding (const cBuilding& building)
876 {
877 	getField (building.getPosition()).removeBuilding (building);
878 
879 	if (building.data.isBig)
880 	{
881 		getField (building.getPosition() + cPosition (1, 0)).removeBuilding (building);
882 		getField (building.getPosition() + cPosition (1, 1)).removeBuilding (building);
883 		getField (building.getPosition() + cPosition (0, 1)).removeBuilding (building);
884 	}
885 	removedUnit (building);
886 }
887 
deleteVehicle(const cVehicle & vehicle)888 void cMap::deleteVehicle (const cVehicle& vehicle)
889 {
890 	if (vehicle.data.factorAir > 0)
891 	{
892 		getField (vehicle.getPosition()).removePlane (vehicle);
893 	}
894 	else
895 	{
896 		getField (vehicle.getPosition()).removeVehicle (vehicle);
897 
898 		if (vehicle.data.isBig)
899 		{
900 			getField (vehicle.getPosition() + cPosition (1, 0)).removeVehicle (vehicle);
901 			getField (vehicle.getPosition() + cPosition (1, 1)).removeVehicle (vehicle);
902 			getField (vehicle.getPosition() + cPosition (0, 1)).removeVehicle (vehicle);
903 		}
904 	}
905 	removedUnit (vehicle);
906 }
907 
deleteUnit(const cUnit & unit)908 void cMap::deleteUnit (const cUnit& unit)
909 {
910 	if (unit.isABuilding())
911 	{
912 		deleteBuilding (static_cast<const cBuilding&> (unit));
913 	}
914 	else
915 	{
916 		assert (unit.isAVehicle());
917 		deleteVehicle (static_cast<const cVehicle&> (unit));
918 	}
919 }
920 
moveVehicle(cVehicle & vehicle,const cPosition & position,int height)921 void cMap::moveVehicle (cVehicle& vehicle, const cPosition& position, int height)
922 {
923 	const auto oldPosition = vehicle.getPosition();
924 
925 	vehicle.setPosition (position);
926 
927 	if (vehicle.data.factorAir > 0)
928 	{
929 		getField (oldPosition).removePlane (vehicle);
930 		height = std::min<int> (getField (position).getPlanes().size(), height);
931 		getField (position).addPlane (vehicle, height);
932 	}
933 	else
934 	{
935 		getField (oldPosition).removeVehicle (vehicle);
936 
937 		//check, whether the vehicle is centered on 4 map fields
938 		if (vehicle.data.isBig)
939 		{
940 			getField (oldPosition + cPosition (1, 0)).removeVehicle (vehicle);
941 			getField (oldPosition + cPosition (1, 1)).removeVehicle (vehicle);
942 			getField (oldPosition + cPosition (0, 1)).removeVehicle (vehicle);
943 
944 			vehicle.data.isBig = false;
945 		}
946 
947 		getField (position).addVehicle (vehicle, 0);
948 	}
949 	movedVehicle (vehicle, oldPosition);
950 }
951 
moveVehicleBig(cVehicle & vehicle,const cPosition & position)952 void cMap::moveVehicleBig (cVehicle& vehicle, const cPosition& position)
953 {
954 	if (vehicle.data.isBig)
955 	{
956 		Log.write ("Calling moveVehicleBig on a big vehicle", cLog::eLOG_TYPE_NET_ERROR);
957 		//calling this function twice is always an error.
958 		//nevertheless try to proceed by resetting the data.isBig flag
959 		moveVehicle (vehicle, position);
960 	}
961 
962 	const auto oldPosition = vehicle.getPosition();
963 
964 	getField (oldPosition).removeVehicle (vehicle);
965 
966 	vehicle.setPosition (position);
967 
968 	getField (position).addVehicle (vehicle, 0);
969 	getField (position + cPosition (1, 0)).addVehicle (vehicle, 0);
970 	getField (position + cPosition (1, 1)).addVehicle (vehicle, 0);
971 	getField (position + cPosition (0, 1)).addVehicle (vehicle, 0);
972 
973 	vehicle.data.isBig = true;
974 
975 	movedVehicle (vehicle, oldPosition);
976 }
977 
possiblePlace(const cVehicle & vehicle,const cPosition & position,bool checkPlayer) const978 bool cMap::possiblePlace (const cVehicle& vehicle, const cPosition& position, bool checkPlayer) const
979 {
980 	return possiblePlaceVehicle (vehicle.data, position, vehicle.getOwner(), checkPlayer);
981 }
982 
possiblePlaceVehicle(const sUnitData & vehicleData,const cPosition & position,const cPlayer * player,bool checkPlayer) const983 bool cMap::possiblePlaceVehicle (const sUnitData& vehicleData, const cPosition& position, const cPlayer* player, bool checkPlayer) const
984 {
985 	if (isValidPosition (position) == false) return false;
986 
987 	const std::vector<cBuilding*>& buildings = getField (position).getBuildings();
988 	std::vector<cBuilding*>::const_iterator b_it = buildings.begin();
989 	std::vector<cBuilding*>::const_iterator b_end = buildings.end();
990 
991 	//search first building, that is not a connector
992 	if (b_it != b_end && (*b_it)->data.surfacePosition == sUnitData::SURFACE_POS_ABOVE) ++b_it;
993 
994 	if (vehicleData.factorAir > 0)
995 	{
996 		if (checkPlayer && player && !player->canSeeAt (position)) return true;
997 		//only one plane per field for now
998 		if (getField (position).getPlanes().size() >= MAX_PLANES_PER_FIELD) return false;
999 	}
1000 	if (vehicleData.factorGround > 0)
1001 	{
1002 		if (isBlocked (position)) return false;
1003 
1004 		if ((isWater (position) && vehicleData.factorSea == 0) ||
1005 			(isCoast (position) && vehicleData.factorCoast == 0))
1006 		{
1007 			if (checkPlayer && player && !player->canSeeAt (position)) return false;
1008 
1009 			//vehicle can drive on water, if there is a bridge, platform or road
1010 			if (b_it == b_end) return false;
1011 			if ((*b_it)->data.surfacePosition != sUnitData::SURFACE_POS_ABOVE_SEA &&
1012 				(*b_it)->data.surfacePosition != sUnitData::SURFACE_POS_BASE &&
1013 				(*b_it)->data.surfacePosition != sUnitData::SURFACE_POS_ABOVE_BASE) return false;
1014 		}
1015 		//check for enemy mines
1016 		if (player && b_it != b_end && (*b_it)->getOwner() != player &&
1017 			(*b_it)->data.explodesOnContact &&
1018 			((*b_it)->isDetectedByPlayer (player) || checkPlayer))
1019 			return false;
1020 
1021 		if (checkPlayer && player && !player->canSeeAt (position)) return true;
1022 
1023 		if (getField (position).getVehicles().empty() == false) return false;
1024 		if (b_it != b_end)
1025 		{
1026 			// only base buildings and rubble is allowed on the same field with a vehicle
1027 			// (connectors have been skiped, so doesn't matter here)
1028 			if ((*b_it)->data.surfacePosition != sUnitData::SURFACE_POS_ABOVE_SEA &&
1029 				(*b_it)->data.surfacePosition != sUnitData::SURFACE_POS_BASE &&
1030 				(*b_it)->data.surfacePosition != sUnitData::SURFACE_POS_ABOVE_BASE &&
1031 				(*b_it)->data.surfacePosition != sUnitData::SURFACE_POS_BENEATH_SEA &&
1032 				(*b_it)->getOwner()) return false;
1033 		}
1034 	}
1035 	else if (vehicleData.factorSea > 0)
1036 	{
1037 		if (isBlocked (position)) return false;
1038 
1039 		if (!isWater (position) &&
1040 			(!isCoast (position) || vehicleData.factorCoast == 0)) return false;
1041 
1042 		//check for enemy mines
1043 		if (player && b_it != b_end && (*b_it)->getOwner() != player &&
1044 			(*b_it)->data.explodesOnContact && (*b_it)->isDetectedByPlayer (player))
1045 			return false;
1046 
1047 		if (checkPlayer && player && !player->canSeeAt (position)) return true;
1048 
1049 		if (getField (position).getVehicles().empty() == false) return false;
1050 
1051 		//only bridge and sea mine are allowed on the same field with a ship (connectors have been skiped, so doesn't matter here)
1052 		if (b_it != b_end &&
1053 			(*b_it)->data.surfacePosition != sUnitData::SURFACE_POS_ABOVE_SEA &&
1054 			(*b_it)->data.surfacePosition != sUnitData::SURFACE_POS_BENEATH_SEA)
1055 		{
1056 			// if the building is a landmine, we have to check whether it's on a bridge or not
1057 			if ((*b_it)->data.surfacePosition == sUnitData::SURFACE_POS_ABOVE_BASE)
1058 			{
1059 				++b_it;
1060 				if (b_it == b_end || (*b_it)->data.surfacePosition != sUnitData::SURFACE_POS_ABOVE_SEA) return false;
1061 			}
1062 			else return false;
1063 		}
1064 	}
1065 	return true;
1066 }
1067 
1068 // can't place it too near to the map border
possiblePlaceBuildingWithMargin(const sUnitData & buildingData,const cPosition & position,int margin,const cVehicle * vehicle) const1069 bool cMap::possiblePlaceBuildingWithMargin (const sUnitData& buildingData, const cPosition& position, int margin, const cVehicle* vehicle) const
1070 {
1071 	if (position.x() < margin || position.x() >= getSize().x() - margin || position.y() < margin || position.y() >= getSize().y() - margin) return false;
1072 	return possiblePlaceBuilding (buildingData, position, vehicle);
1073 }
1074 
possiblePlaceBuilding(const sUnitData & buildingData,const cPosition & position,const cVehicle * vehicle) const1075 bool cMap::possiblePlaceBuilding (const sUnitData& buildingData, const cPosition& position, const cVehicle* vehicle) const
1076 {
1077 	if (!isValidPosition (position)) return false;
1078 	if (isBlocked (position)) return false;
1079 	const cMapField& field = getField (position);
1080 
1081 	// Check all buildings in this field for a building of the same type. This
1082 	// will prevent roads, connectors and water platforms from building on top
1083 	// of themselves.
1084 	const std::vector<cBuilding*>& buildings = field.getBuildings();
1085 	for (const cBuilding* building : buildings)
1086 	{
1087 		if (building->data.ID == buildingData.ID)
1088 		{
1089 			return false;
1090 		}
1091 	}
1092 
1093 	// Determine terrain type
1094 	bool water = isWater (position);
1095 	bool coast = isCoast (position);
1096 	bool ground = !water && !coast;
1097 
1098 	for (const cBuilding* building : buildings)
1099 	{
1100 		if (buildingData.surfacePosition == building->data.surfacePosition &&
1101 			building->data.canBeOverbuild == sUnitData::OVERBUILD_TYPE_NO) return false;
1102 		switch (building->data.surfacePosition)
1103 		{
1104 			case sUnitData::SURFACE_POS_GROUND:
1105 			case sUnitData::SURFACE_POS_ABOVE_SEA: // bridge
1106 				if (buildingData.surfacePosition != sUnitData::SURFACE_POS_ABOVE &&
1107 					buildingData.surfacePosition != sUnitData::SURFACE_POS_BASE && // mine can be placed on bridge
1108 					buildingData.surfacePosition != sUnitData::SURFACE_POS_BENEATH_SEA && // seamine can be placed under bridge
1109 					building->data.canBeOverbuild == sUnitData::OVERBUILD_TYPE_NO) return false;
1110 				break;
1111 			case sUnitData::SURFACE_POS_BENEATH_SEA: // seamine
1112 			case sUnitData::SURFACE_POS_ABOVE_BASE:  // landmine
1113 				// mine must be removed first
1114 				if (buildingData.surfacePosition != sUnitData::SURFACE_POS_ABOVE) return false;
1115 				break;
1116 			case sUnitData::SURFACE_POS_BASE: // platform, road
1117 				water = coast = false;
1118 				ground = true;
1119 				break;
1120 			case sUnitData::SURFACE_POS_ABOVE: // connector
1121 				break;
1122 		}
1123 	}
1124 	if ((water && buildingData.factorSea == 0) ||
1125 		(coast && buildingData.factorCoast == 0) ||
1126 		(ground && buildingData.factorGround == 0)) return false;
1127 
1128 	//can not build on rubble
1129 	if (field.getRubble() &&
1130 		buildingData.surfacePosition != sUnitData::SURFACE_POS_ABOVE &&
1131 		buildingData.surfacePosition != sUnitData::SURFACE_POS_ABOVE_BASE) return false;
1132 
1133 	if (field.getVehicles().empty() == false)
1134 	{
1135 		if (!vehicle) return false;
1136 		if (vehicle != field.getVehicles()[0]) return false;
1137 	}
1138 	return true;
1139 }
reset()1140 void cMap::reset()
1141 {
1142 	for (int i = 0; i < getSize().x() * getSize().y(); i++)
1143 	{
1144 		fields[i].removeAll();
1145 	}
1146 }
1147 
getResourceDensityFactor(eGameSettingsResourceDensity density)1148 /*static*/ int cMap::getResourceDensityFactor (eGameSettingsResourceDensity density)
1149 {
1150 	switch (density)
1151 	{
1152 		case eGameSettingsResourceDensity::Sparse:
1153 			return 0;
1154 		case eGameSettingsResourceDensity::Normal:
1155 			return 1;
1156 		case eGameSettingsResourceDensity::Dense:
1157 			return 2;
1158 		case eGameSettingsResourceDensity::TooMuch:
1159 			return 3;
1160 	}
1161 	assert (false);
1162 	return 0;
1163 }
1164 
getResourceAmountFactor(eGameSettingsResourceAmount amount)1165 /*static*/int cMap::getResourceAmountFactor (eGameSettingsResourceAmount amount)
1166 {
1167 	switch (amount)
1168 	{
1169 		case eGameSettingsResourceAmount::Limited:
1170 			return 0;
1171 		case eGameSettingsResourceAmount::Normal:
1172 			return 1;
1173 		case eGameSettingsResourceAmount::High:
1174 			return 2;
1175 		case eGameSettingsResourceAmount::TooMuch:
1176 			return 3;
1177 	}
1178 	assert (false);
1179 	return 0;
1180 }
1181