1 //       _________ __                 __
2 //      /   _____//  |_____________ _/  |______     ____  __ __  ______
3 //      \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
4 //      /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ |
5 //     /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
6 //             \/                  \/          \//_____/            \/
7 //  ______________________                           ______________________
8 //                        T H E   W A R   B E G I N S
9 //         Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name map.cpp - The map source file. */
12 //
13 //      (c) Copyright 1998-2019 by Lutz Sammer, Vladi Shabanski,
14 //                                 Francois Beerten and Andrettin
15 //
16 //      This program is free software; you can redistribute it and/or modify
17 //      it under the terms of the GNU General Public License as published by
18 //      the Free Software Foundation; only version 2 of the License.
19 //
20 //      This program is distributed in the hope that it will be useful,
21 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
22 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 //      GNU General Public License for more details.
24 //
25 //      You should have received a copy of the GNU General Public License
26 //      along with this program; if not, write to the Free Software
27 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 //      02111-1307, USA.
29 //
30 
31 //@{
32 
33 /*----------------------------------------------------------------------------
34 --  Includes
35 ----------------------------------------------------------------------------*/
36 
37 #include "stratagus.h"
38 
39 #include "map/map.h"
40 
41 //Wyrmgus start
42 #include <fstream>
43 //Wyrmgus end
44 
45 //Wyrmgus start
46 #include "editor.h"
47 #include "game.h" // for the SaveGameLoading variable
48 //Wyrmgus end
49 #include "iolib.h"
50 #include "map/map_layer.h"
51 #include "map/map_template.h"
52 #include "map/site.h"
53 #include "map/terrain_type.h"
54 #include "map/tileset.h"
55 #include "plane.h"
56 #include "player.h"
57 //Wyrmgus start
58 #include "province.h"
59 #include "quest.h"
60 #include "settings.h"
61 #include "sound_server.h"
62 //Wyrmgus end
63 #include "time/calendar.h"
64 #include "time/season.h"
65 #include "time/season_schedule.h"
66 #include "time/time_of_day.h"
67 #include "time/time_of_day_schedule.h"
68 //Wyrmgus start
69 #include "translate.h"
70 //Wyrmgus end
71 #include "ui/ui.h"
72 #include "unit/unit.h"
73 //Wyrmgus start
74 #include "unit/unit_find.h"
75 //Wyrmgus end
76 #include "unit/unit_manager.h"
77 //Wyrmgus start
78 #include "upgrade/upgrade.h"
79 //Wyrmgus end
80 #include "version.h"
81 #include "video.h"
82 #include "world.h"
83 
84 #ifdef USE_OAML
85 #include <oaml.h>
86 
87 extern oamlApi *oaml;
88 extern bool enableOAML;
89 #endif
90 
91 /*----------------------------------------------------------------------------
92 --  Variables
93 ----------------------------------------------------------------------------*/
94 
95 //Wyrmgus start
96 std::vector<CTerrainFeature *> TerrainFeatures;
97 std::map<std::string, CTerrainFeature *> TerrainFeatureIdentToPointer;
98 std::map<std::tuple<int, int, int>, int> TerrainFeatureColorToIndex;
99 //Wyrmgus end
100 CMap Map;                   /// The current map
101 int FlagRevealMap;          /// Flag must reveal the map
102 int ReplayRevealMap;        /// Reveal Map is replay
103 int ForestRegeneration;     /// Forest regeneration
104 char CurrentMapPath[1024];  /// Path of the current map
105 
106 //Wyrmgus start
107 /**
108 **  Get a terrain feature
109 */
GetTerrainFeature(const std::string & terrain_feature_ident)110 CTerrainFeature *GetTerrainFeature(const std::string &terrain_feature_ident)
111 {
112 	if (terrain_feature_ident.empty()) {
113 		return nullptr;
114 	}
115 
116 	std::map<std::string, CTerrainFeature *>::const_iterator find_iterator = TerrainFeatureIdentToPointer.find(terrain_feature_ident);
117 
118 	if (find_iterator != TerrainFeatureIdentToPointer.end()) {
119 		return find_iterator->second;
120 	}
121 
122 	return nullptr;
123 }
124 //Wyrmgus end
125 
126 /*----------------------------------------------------------------------------
127 --  Visible and explored handling
128 ----------------------------------------------------------------------------*/
129 
130 /**
131 **  Marks seen tile -- used mainly for the Fog Of War
132 **
133 **  @param mf  MapField-position.
134 */
135 //Wyrmgus start
136 //void CMap::MarkSeenTile(CMapField &mf)
MarkSeenTile(CMapField & mf,int z)137 void CMap::MarkSeenTile(CMapField &mf, int z)
138 //Wyrmgus end
139 {
140 	//Wyrmgus start
141 //	const unsigned int tile = mf.getGraphicTile();
142 //	const unsigned int seentile = mf.playerInfo.SeenTile;
143 	//Wyrmgus end
144 
145 	//  Nothing changed? Seeing already the correct tile.
146 	//Wyrmgus start
147 //	if (tile == seentile) {
148 	if (mf.IsSeenTileCorrect()) {
149 	//Wyrmgus end
150 		return;
151 	}
152 	//Wyrmgus start
153 //	mf.playerInfo.SeenTile = tile;
154 	mf.UpdateSeenTile();
155 	//Wyrmgus end
156 
157 #ifdef MINIMAP_UPDATE
158 	//rb - GRRRRRRRRRRRR
159 	//Wyrmgus start
160 //	const unsigned int index = &mf - Map.Fields;
161 //	const int y = index / Info.MapWidth;
162 //	const int x = index - (y * Info.MapWidth);
163 	const CMapLayer *map_layer = Map.MapLayers[z];
164 	const unsigned int index = &mf - map_layer->Fields;
165 	const int y = index / map_layer->GetWidth();
166 	const int x = index - (y * map_layer->GetWidth());
167 	//Wyrmgus end
168 	const Vec2i pos = {x, y}
169 #endif
170 
171 	//Wyrmgus start
172 	/*
173 	if (this->Tileset->TileTypeTable.empty() == false) {
174 #ifndef MINIMAP_UPDATE
175 		//rb - GRRRRRRRRRRRR
176 		const unsigned int index = &mf - Map.Fields;
177 		const int y = index / Info.MapWidth;
178 		const int x = index - (y * Info.MapWidth);
179 		const Vec2i pos(x, y);
180 #endif
181 
182 		//  Handle wood changes. FIXME: check if for growing wood correct?
183 		if (tile == this->Tileset->getRemovedTreeTile()) {
184 			FixNeighbors(MapFieldForest, 1, pos);
185 		} else if (seentile == this->Tileset->getRemovedTreeTile()) {
186 			FixTile(MapFieldForest, 1, pos);
187 		} else if (mf.ForestOnMap()) {
188 			FixTile(MapFieldForest, 1, pos);
189 			FixNeighbors(MapFieldForest, 1, pos);
190 
191 			// Handle rock changes.
192 		} else if (tile == Tileset->getRemovedRockTile()) {
193 			FixNeighbors(MapFieldRocks, 1, pos);
194 		} else if (seentile == Tileset->getRemovedRockTile()) {
195 			FixTile(MapFieldRocks, 1, pos);
196 		} else if (mf.RockOnMap()) {
197 			FixTile(MapFieldRocks, 1, pos);
198 			FixNeighbors(MapFieldRocks, 1, pos);
199 
200 			//  Handle Walls changes.
201 		} else if (this->Tileset->isAWallTile(tile)
202 				   || this->Tileset->isAWallTile(seentile)) {
203 		//Wyrmgus end
204 			MapFixSeenWallTile(pos);
205 			MapFixSeenWallNeighbors(pos);
206 		}
207 	}
208 	*/
209 	//Wyrmgus end
210 
211 #ifdef MINIMAP_UPDATE
212 	//Wyrmgus start
213 //	UI.Minimap.UpdateXY(pos);
214 	UI.Minimap.UpdateXY(pos, z);
215 	//Wyrmgus end
216 #endif
217 }
218 
219 /**
220 **  Reveal the entire map.
221 */
222 //Wyrmgus start
223 //void CMap::Reveal()
Reveal(bool only_person_players)224 void CMap::Reveal(bool only_person_players)
225 //Wyrmgus end
226 {
227 	//  Mark every explored tile as visible. 1 turns into 2.
228 	//Wyrmgus start
229 	/*
230 	for (int i = 0; i != this->Info.MapWidth * this->Info.MapHeight; ++i) {
231 		CMapField &mf = *this->Field(i);
232 		CMapFieldPlayerInfo &playerInfo = mf.playerInfo;
233 		for (int p = 0; p < PlayerMax; ++p) {
234 			//Wyrmgus start
235 //			playerInfo.Visible[p] = std::max<unsigned short>(1, playerInfo.Visible[p]);
236 			if (Players[p].Type == PlayerPerson || !only_person_players) {
237 				playerInfo.Visible[p] = std::max<unsigned short>(1, playerInfo.Visible[p]);
238 			}
239 			//Wyrmgus end
240 		}
241 		MarkSeenTile(mf);
242 	}
243 	*/
244 	for (size_t z = 0; z < this->MapLayers.size(); ++z) {
245 		for (int i = 0; i != this->Info.MapWidths[z] * this->Info.MapHeights[z]; ++i) {
246 			CMapField &mf = *this->Field(i, z);
247 			CMapFieldPlayerInfo &playerInfo = mf.playerInfo;
248 			for (int p = 0; p < PlayerMax; ++p) {
249 				if (Players[p].Type == PlayerPerson || !only_person_players) {
250 					playerInfo.Visible[p] = std::max<unsigned short>(1, playerInfo.Visible[p]);
251 				}
252 			}
253 			MarkSeenTile(mf, z);
254 		}
255 	}
256 	//Wyrmgus end
257 	//  Global seen recount. Simple and effective.
258 	for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) {
259 		CUnit &unit = **it;
260 		//  Reveal neutral buildings. Gold mines:)
261 		if (unit.Player->Type == PlayerNeutral) {
262 			for (int p = 0; p < PlayerMax; ++p) {
263 				//Wyrmgus start
264 //				if (Players[p].Type != PlayerNobody && (!(unit.Seen.ByPlayer & (1 << p)))) {
265 				if (Players[p].Type != PlayerNobody && (Players[p].Type == PlayerPerson || !only_person_players) && (!(unit.Seen.ByPlayer & (1 << p)))) {
266 				//Wyrmgus end
267 					UnitGoesOutOfFog(unit, Players[p]);
268 					UnitGoesUnderFog(unit, Players[p]);
269 				}
270 			}
271 		}
272 		UnitCountSeen(unit);
273 	}
274 }
275 
276 /*----------------------------------------------------------------------------
277 --  Map queries
278 ----------------------------------------------------------------------------*/
279 
MapPixelPosToTilePos(const PixelPos & mapPos,const int map_layer) const280 Vec2i CMap::MapPixelPosToTilePos(const PixelPos &mapPos, const int map_layer) const
281 {
282 	const Vec2i tilePos(mapPos.x / GetMapLayerPixelTileSize(map_layer).x, mapPos.y / GetMapLayerPixelTileSize(map_layer).y);
283 
284 	return tilePos;
285 }
286 
TilePosToMapPixelPos_TopLeft(const Vec2i & tilePos,const CMapLayer * map_layer) const287 PixelPos CMap::TilePosToMapPixelPos_TopLeft(const Vec2i &tilePos, const CMapLayer *map_layer) const
288 {
289 	PixelPos mapPixelPos(tilePos.x * GetMapLayerPixelTileSize(map_layer ? map_layer->ID : -1).x, tilePos.y * GetMapLayerPixelTileSize(map_layer ? map_layer->ID : -1).y);
290 
291 	return mapPixelPos;
292 }
293 
TilePosToMapPixelPos_Center(const Vec2i & tilePos,const CMapLayer * map_layer) const294 PixelPos CMap::TilePosToMapPixelPos_Center(const Vec2i &tilePos, const CMapLayer *map_layer) const
295 {
296 	return TilePosToMapPixelPos_TopLeft(tilePos, map_layer) + GetMapLayerPixelTileSize(map_layer ? map_layer->ID : -1) / 2;
297 }
298 
299 //Wyrmgus start
GetTileTerrain(const Vec2i & pos,const bool overlay,const int z) const300 CTerrainType *CMap::GetTileTerrain(const Vec2i &pos, const bool overlay, const int z) const
301 {
302 	if (!Map.Info.IsPointOnMap(pos, z)) {
303 		return nullptr;
304 	}
305 
306 	CMapField &mf = *this->Field(pos, z);
307 
308 	return mf.GetTerrain(overlay);
309 }
310 
GetTileTopTerrain(const Vec2i & pos,const bool seen,const int z,const bool ignore_destroyed) const311 CTerrainType *CMap::GetTileTopTerrain(const Vec2i &pos, const bool seen, const int z, const bool ignore_destroyed) const
312 {
313 	if (!Map.Info.IsPointOnMap(pos, z)) {
314 		return nullptr;
315 	}
316 
317 	CMapField &mf = *this->Field(pos, z);
318 
319 	return mf.GetTopTerrain();
320 }
321 
GetTileLandmass(const Vec2i & pos,int z) const322 int CMap::GetTileLandmass(const Vec2i &pos, int z) const
323 {
324 	if (!Map.Info.IsPointOnMap(pos, z)) {
325 		return 0;
326 	}
327 
328 	CMapField &mf = *this->Field(pos, z);
329 
330 	return mf.Landmass;
331 }
332 
GenerateUnitLocation(const CUnitType * unit_type,const CFaction * faction,const Vec2i & min_pos,const Vec2i & max_pos,const int z) const333 Vec2i CMap::GenerateUnitLocation(const CUnitType *unit_type, const CFaction *faction, const Vec2i &min_pos, const Vec2i &max_pos, const int z) const
334 {
335 	if (SaveGameLoading) {
336 		return Vec2i(-1, -1);
337 	}
338 
339 	CPlayer *player = GetFactionPlayer(faction);
340 
341 	Vec2i random_pos(-1, -1);
342 
343 	std::vector<CTerrainType *> allowed_terrains;
344 	if (unit_type->BoolFlag[FAUNA_INDEX].value && unit_type->Species) { //if the unit is a fauna one, it has to start on terrain it is native to
345 		for (size_t i = 0; i < unit_type->Species->Terrains.size(); ++i) {
346 			allowed_terrains.push_back(unit_type->Species->Terrains[i]);
347 		}
348 	}
349 
350 	for (size_t i = 0; i < unit_type->SpawnUnits.size(); ++i) {
351 		CUnitType *spawned_type = unit_type->SpawnUnits[i];
352 		if (spawned_type->BoolFlag[FAUNA_INDEX].value && spawned_type->Species) {
353 			for (size_t j = 0; j < spawned_type->Species->Terrains.size(); ++j) {
354 				allowed_terrains.push_back(spawned_type->Species->Terrains[j]);
355 			}
356 		}
357 	}
358 
359 	std::vector<Vec2i> potential_positions;
360 	for (int x = min_pos.x; x <= max_pos.x; ++x) {
361 		for (int y = min_pos.y; y <= max_pos.y; ++y) {
362 			potential_positions.push_back(Vec2i(x, y));
363 		}
364 	}
365 
366 	while (!potential_positions.empty()) {
367 		random_pos = potential_positions[SyncRand(potential_positions.size())];
368 		potential_positions.erase(std::remove(potential_positions.begin(), potential_positions.end(), random_pos), potential_positions.end());
369 
370 		if (!this->Info.IsPointOnMap(random_pos, z) || (this->IsPointInASubtemplateArea(random_pos, z) && GameCycle == 0)) {
371 			continue;
372 		}
373 
374 		if (allowed_terrains.size() > 0 && std::find(allowed_terrains.begin(), allowed_terrains.end(), GetTileTopTerrain(random_pos, false, z)) == allowed_terrains.end()) { //if the unit is a fauna one, it has to start on terrain it is native to
375 			continue;
376 		}
377 
378 		std::vector<CUnit *> table;
379 		if (player != nullptr) {
380 			Select(random_pos - Vec2i(32, 32), random_pos + Vec2i(unit_type->TileSize.x - 1, unit_type->TileSize.y - 1) + Vec2i(32, 32), table, z, MakeAndPredicate(HasNotSamePlayerAs(*player), HasNotSamePlayerAs(Players[PlayerNumNeutral])));
381 		} else if (!unit_type->GivesResource) {
382 			if (unit_type->BoolFlag[PREDATOR_INDEX].value || (unit_type->BoolFlag[PEOPLEAVERSION_INDEX].value && unit_type->UnitType == UnitTypeFly)) {
383 				Select(random_pos - Vec2i(16, 16), random_pos + Vec2i(unit_type->TileSize.x - 1, unit_type->TileSize.y - 1) + Vec2i(16, 16), table, z, MakeOrPredicate(HasNotSamePlayerAs(Players[PlayerNumNeutral]), HasSameTypeAs(*SettlementSiteUnitType)));
384 			} else {
385 				Select(random_pos - Vec2i(8, 8), random_pos + Vec2i(unit_type->TileSize.x - 1, unit_type->TileSize.y - 1) + Vec2i(8, 8), table, z, HasNotSamePlayerAs(Players[PlayerNumNeutral]));
386 			}
387 		} else if (unit_type->GivesResource && !unit_type->BoolFlag[BUILDING_INDEX].value) { //for non-building resources (i.e. wood piles), place them within a certain distance of player units, to prevent them from blocking the way
388 			Select(random_pos - Vec2i(4, 4), random_pos + Vec2i(unit_type->TileSize.x - 1, unit_type->TileSize.y - 1) + Vec2i(4, 4), table, z, HasNotSamePlayerAs(Players[PlayerNumNeutral]));
389 		}
390 
391 		if (table.size() == 0) {
392 			bool passable_surroundings = true; //check if the unit won't be placed next to unpassable terrain
393 			for (int x = random_pos.x - 1; x < random_pos.x + unit_type->TileSize.x + 1; ++x) {
394 				for (int y = random_pos.y - 1; y < random_pos.y + unit_type->TileSize.y + 1; ++y) {
395 					if (Map.Info.IsPointOnMap(x, y, z) && Map.Field(x, y, z)->CheckMask(MapFieldUnpassable)) {
396 						passable_surroundings = false;
397 					}
398 				}
399 			}
400 			if (passable_surroundings && UnitTypeCanBeAt(*unit_type, random_pos, z) && (!unit_type->BoolFlag[BUILDING_INDEX].value || CanBuildUnitType(nullptr, *unit_type, random_pos, 0, true, z))) {
401 				return random_pos;
402 			}
403 		}
404 	}
405 
406 	return Vec2i(-1, -1);
407 }
408 //Wyrmgus end
409 
410 /**
411 **  Wall on map tile.
412 **
413 **  @param pos  map tile position.
414 **
415 **  @return    True if wall, false otherwise.
416 */
WallOnMap(const Vec2i & pos,int z) const417 bool CMap::WallOnMap(const Vec2i &pos, int z) const
418 {
419 	Assert(Map.Info.IsPointOnMap(pos, z));
420 	return Field(pos, z)->isAWall();
421 }
422 
423 //Wyrmgus start
CurrentTerrainCanBeAt(const Vec2i & pos,bool overlay,int z)424 bool CMap::CurrentTerrainCanBeAt(const Vec2i &pos, bool overlay, int z)
425 {
426 	CMapField &mf = *this->Field(pos, z);
427 	CTerrainType *terrain = nullptr;
428 
429 	if (overlay) {
430 		terrain = mf.OverlayTerrain;
431 	} else {
432 		terrain = mf.Terrain;
433 	}
434 
435 	if (!terrain) {
436 		return true;
437 	}
438 
439 	if (terrain->AllowSingle) {
440 		return true;
441 	}
442 
443 	std::vector<int> transition_directions;
444 
445 	for (int x_offset = -1; x_offset <= 1; ++x_offset) {
446 		for (int y_offset = -1; y_offset <= 1; ++y_offset) {
447 			if (x_offset != 0 || y_offset != 0) {
448 				Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
449 				if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
450 					CMapField &adjacent_mf = *this->Field(adjacent_pos, z);
451 
452 					CTerrainType *adjacent_terrain = this->GetTileTerrain(adjacent_pos, overlay, z);
453 					if (overlay && adjacent_terrain && this->Field(adjacent_pos, z)->OverlayTerrainDestroyed) {
454 						adjacent_terrain = nullptr;
455 					}
456 					if (terrain != adjacent_terrain) { // also happens if terrain is null, so that i.e. tree transitions display correctly when adjacent to tiles without overlays
457 						transition_directions.push_back(GetDirectionFromOffset(x_offset, y_offset));
458 					}
459 				}
460 			}
461 		}
462 	}
463 
464 	if (std::find(transition_directions.begin(), transition_directions.end(), North) != transition_directions.end() && std::find(transition_directions.begin(), transition_directions.end(), South) != transition_directions.end()) {
465 		return false;
466 	} else if (std::find(transition_directions.begin(), transition_directions.end(), West) != transition_directions.end() && std::find(transition_directions.begin(), transition_directions.end(), East) != transition_directions.end()) {
467 		return false;
468 	}
469 
470 	return true;
471 }
472 
473 /**
474 **	@brief	Get whether a given tile borders only tiles with the same terrain as itself
475 **
476 **	@param	pos					The tile's position
477 **	@param	new_terrain_type	The potential new terrain type for the tile
478 **	@param	z					The tile's map layer
479 **
480 **	@return	True if the tile borders only tiles with the same terrain as itself, false otherwise
481 */
TileBordersOnlySameTerrain(const Vec2i & pos,const CTerrainType * new_terrain_type,const int z)482 bool CMap::TileBordersOnlySameTerrain(const Vec2i &pos, const CTerrainType *new_terrain_type, const int z)
483 {
484 	for (int sub_x = -1; sub_x <= 1; ++sub_x) {
485 		for (int sub_y = -1; sub_y <= 1; ++sub_y) {
486 			Vec2i adjacent_pos(pos.x + sub_x, pos.y + sub_y);
487 			if (!this->Info.IsPointOnMap(adjacent_pos, z) || (sub_x == 0 && sub_y == 0)) {
488 				continue;
489 			}
490 			if (this->IsPointInASubtemplateArea(pos, z) && !this->IsPointInASubtemplateArea(adjacent_pos, z)) {
491 				continue;
492 			}
493 			CTerrainType *top_terrain = GetTileTopTerrain(pos, false, z);
494 			CTerrainType *adjacent_top_terrain = GetTileTopTerrain(adjacent_pos, false, z);
495 			if (!new_terrain_type->Overlay) {
496 				if (
497 					adjacent_top_terrain
498 					&& adjacent_top_terrain != top_terrain
499 					&& (std::find(top_terrain->InnerBorderTerrains.begin(), top_terrain->InnerBorderTerrains.end(), adjacent_top_terrain) == top_terrain->InnerBorderTerrains.end() || std::find(new_terrain_type->InnerBorderTerrains.begin(), new_terrain_type->InnerBorderTerrains.end(), adjacent_top_terrain) == new_terrain_type->InnerBorderTerrains.end())
500 					&& adjacent_top_terrain != new_terrain_type
501 				) {
502 					return false;
503 				}
504 			} else {
505 				if (
506 					adjacent_top_terrain
507 					&& adjacent_top_terrain != top_terrain
508 					&& std::find(top_terrain->BaseTerrainTypes.begin(), top_terrain->BaseTerrainTypes.end(), adjacent_top_terrain) == top_terrain->BaseTerrainTypes.end() && std::find(adjacent_top_terrain->BaseTerrainTypes.begin(), adjacent_top_terrain->BaseTerrainTypes.end(), top_terrain) == adjacent_top_terrain->BaseTerrainTypes.end()
509 					&& adjacent_top_terrain != new_terrain_type
510 				) {
511 					return false;
512 				}
513 			}
514 		}
515 	}
516 
517 	return true;
518 }
519 
TileBordersFlag(const Vec2i & pos,int z,int flag,bool reverse)520 bool CMap::TileBordersFlag(const Vec2i &pos, int z, int flag, bool reverse)
521 {
522 	for (int sub_x = -1; sub_x <= 1; ++sub_x) {
523 		for (int sub_y = -1; sub_y <= 1; ++sub_y) {
524 			Vec2i adjacent_pos(pos.x + sub_x, pos.y + sub_y);
525 			if (!this->Info.IsPointOnMap(adjacent_pos, z) || (sub_x == 0 && sub_y == 0)) {
526 				continue;
527 			}
528 			CMapField &mf = *Map.Field(adjacent_pos, z);
529 
530 			if ((!reverse && mf.CheckMask(flag)) || (reverse && !mf.CheckMask(flag))) {
531 				return true;
532 			}
533 		}
534 	}
535 
536 	return false;
537 }
538 
TileBordersBuilding(const Vec2i & pos,int z)539 bool CMap::TileBordersBuilding(const Vec2i &pos, int z)
540 {
541 	for (int sub_x = -1; sub_x <= 1; ++sub_x) {
542 		for (int sub_y = -1; sub_y <= 1; ++sub_y) {
543 			Vec2i adjacent_pos(pos.x + sub_x, pos.y + sub_y);
544 			if (!this->Info.IsPointOnMap(adjacent_pos, z) || (sub_x == 0 && sub_y == 0)) {
545 				continue;
546 			}
547 			CMapField &mf = *Map.Field(adjacent_pos, z);
548 
549 			if (mf.CheckMask(MapFieldBuilding)) {
550 				return true;
551 			}
552 		}
553 	}
554 
555 	return false;
556 }
557 
TileBordersPathway(const Vec2i & pos,int z,bool only_railroad)558 bool CMap::TileBordersPathway(const Vec2i &pos, int z, bool only_railroad)
559 {
560 	for (int sub_x = -1; sub_x <= 1; ++sub_x) {
561 		for (int sub_y = -1; sub_y <= 1; ++sub_y) {
562 			Vec2i adjacent_pos(pos.x + sub_x, pos.y + sub_y);
563 			if (!this->Info.IsPointOnMap(adjacent_pos, z) || (sub_x == 0 && sub_y == 0)) {
564 				continue;
565 			}
566 			CMapField &mf = *Map.Field(adjacent_pos, z);
567 
568 			if (
569 				(!only_railroad && mf.CheckMask(MapFieldRoad))
570 				|| mf.CheckMask(MapFieldRailroad)
571 			) {
572 				return true;
573 			}
574 		}
575 	}
576 
577 	return false;
578 }
579 
TileBordersUnit(const Vec2i & pos,int z)580 bool CMap::TileBordersUnit(const Vec2i &pos, int z)
581 {
582 	for (int sub_x = -1; sub_x <= 1; ++sub_x) {
583 		for (int sub_y = -1; sub_y <= 1; ++sub_y) {
584 			Vec2i adjacent_pos(pos.x + sub_x, pos.y + sub_y);
585 			if (!this->Info.IsPointOnMap(adjacent_pos, z) || (sub_x == 0 && sub_y == 0)) {
586 				continue;
587 			}
588 			CMapField &mf = *Map.Field(adjacent_pos, z);
589 
590 			const CUnitCache &cache = mf.UnitCache;
591 			for (size_t i = 0; i != cache.size(); ++i) {
592 				CUnit &unit = *cache[i];
593 				if (unit.IsAliveOnMap()) {
594 					return true;
595 				}
596 			}
597 		}
598 	}
599 
600 	return false;
601 }
602 
603 /**
604 **	@brief	Get whether the given tile has any bordering terrains which are incompatible with a given terrain type
605 **
606 **	@param	pos					The tile's position
607 **	@param	new_terrain_type	The terrain type to check
608 **	@param	z					The tile's map layer
609 **
610 **	@return	True if the tile borders only tiles with the same terrain as itself, false otherwise
611 */
TileBordersTerrainIncompatibleWithTerrain(const Vec2i & pos,const CTerrainType * terrain_type,const int z)612 bool CMap::TileBordersTerrainIncompatibleWithTerrain(const Vec2i &pos, const CTerrainType *terrain_type, const int z)
613 {
614 	if (!terrain_type || !terrain_type->Overlay) {
615 		return false;
616 	}
617 
618 	CTerrainType *tile_terrain = this->GetTileTerrain(pos, false, z);
619 
620 	for (int sub_x = -1; sub_x <= 1; ++sub_x) {
621 		for (int sub_y = -1; sub_y <= 1; ++sub_y) {
622 			Vec2i adjacent_pos(pos.x + sub_x, pos.y + sub_y);
623 
624 			if (!this->Info.IsPointOnMap(adjacent_pos, z) || (sub_x == 0 && sub_y == 0)) {
625 				continue;
626 			}
627 
628 			CTerrainType *adjacent_terrain = this->GetTileTerrain(adjacent_pos, false, z);
629 
630 			if (tile_terrain == adjacent_terrain) {
631 				continue;
632 			}
633 
634 			if (terrain_type->Overlay) {
635 				if ( //if the terrain type is an overlay one, the adjacent tile terrain is incompatible with it if it both cannot be a base terrain for the overlay terrain type, and it "expands into" the tile (that is, the tile has the adjacent terrain as an inner border terrain)
636 					std::find(tile_terrain->InnerBorderTerrains.begin(), tile_terrain->InnerBorderTerrains.end(), adjacent_terrain) != tile_terrain->InnerBorderTerrains.end()
637 					&& std::find(terrain_type->BaseTerrainTypes.begin(), terrain_type->BaseTerrainTypes.end(), adjacent_terrain) == terrain_type->BaseTerrainTypes.end()
638 				) {
639 					return true;
640 				}
641 			} else {
642 				//if the terrain type is not an overlay one, the adjacent tile terrain is incompatible with it if it cannot border the terrain type
643 				if (std::find(terrain_type->BorderTerrains.begin(), terrain_type->BorderTerrains.end(), adjacent_terrain) == terrain_type->BorderTerrains.end()) {
644 					return true;
645 				}
646 			}
647 		}
648 	}
649 
650 	return false;
651 }
652 
653 /**
654 **	@brief	Get whether a tile has units that are incompatible with a given terrain type
655 **
656 **	@param	pos				The tile's position
657 **	@param	terrain_type	The terrain type
658 **	@param	z				The tile's map layer
659 **
660 **	@return	Whether the tile has units that are incompatible with the given terrain type
661 */
TileHasUnitsIncompatibleWithTerrain(const Vec2i & pos,const CTerrainType * terrain_type,const int z)662 bool CMap::TileHasUnitsIncompatibleWithTerrain(const Vec2i &pos, const CTerrainType *terrain_type, const int z)
663 {
664 	CMapField &mf = *Map.Field(pos, z);
665 
666 	const CUnitCache &cache = mf.UnitCache;
667 	for (size_t i = 0; i != cache.size(); ++i) {
668 		const CUnit &unit = *cache[i];
669 		if (unit.IsAliveOnMap() && (terrain_type->Flags & unit.Type->MovementMask) != 0) {
670 			return true;
671 		}
672 	}
673 
674 	return false;
675 }
676 
677 /**
678 **	@brief	Get whether a given tile is in a subtemplate area
679 **
680 **	@param	pos				The tile's position
681 **	@param	z				The tile's map layer
682 **	@param	subtemplate		Optional subtemplate argument, if not null then will only return true if the point is in that specific subtemplate area; if it is null, then true will be returned if the point is in any subtemplate area
683 **
684 **	@return	True if the tile is in a subtemplate area, or false otherwise
685 */
IsPointInASubtemplateArea(const Vec2i & pos,const int z,const CMapTemplate * subtemplate) const686 bool CMap::IsPointInASubtemplateArea(const Vec2i &pos, const int z, const CMapTemplate *subtemplate) const
687 {
688 	for (size_t i = 0; i < this->MapLayers[z]->SubtemplateAreas.size(); ++i) {
689 		if (subtemplate && subtemplate != std::get<2>(this->MapLayers[z]->SubtemplateAreas[i])) {
690 			continue;
691 		}
692 
693 		Vec2i min_pos = std::get<0>(this->MapLayers[z]->SubtemplateAreas[i]);
694 		Vec2i max_pos = std::get<1>(this->MapLayers[z]->SubtemplateAreas[i]);
695 		if (pos.x >= min_pos.x && pos.y >= min_pos.y && pos.x <= max_pos.x && pos.y <= max_pos.y) {
696 			return true;
697 		}
698 	}
699 
700 	return false;
701 }
702 
703 /**
704 **	@brief	Get the applied map position of a given subtemplate
705 **
706 **	@param	subtemplate		The subtemplate
707 **
708 **	@return	The subtemplate's position if found, or (-1, -1) otherwise
709 */
GetSubtemplatePos(const CMapTemplate * subtemplate) const710 Vec2i CMap::GetSubtemplatePos(const CMapTemplate *subtemplate) const
711 {
712 	if (!subtemplate) {
713 		return Vec2i(-1, -1);
714 	}
715 
716 	const CMapTemplate *main_template = subtemplate->GetTopMapTemplate();
717 	if (main_template && subtemplate != main_template && main_template->Plane && main_template->World) {
718 		const int z = GetMapLayer(main_template->Plane->Ident, main_template->World->Ident, main_template->SurfaceLayer);
719 		if (z != -1) {
720 			for (size_t i = 0; i < this->MapLayers[z]->SubtemplateAreas.size(); ++i) {
721 				if (subtemplate == std::get<2>(this->MapLayers[z]->SubtemplateAreas[i])) {
722 					return std::get<0>(Map.MapLayers[z]->SubtemplateAreas[i]);
723 				}
724 			}
725 		}
726 	}
727 
728 	return Vec2i(-1, -1);
729 }
730 
731 /**
732 **	@brief	Get the applied end map position of a given subtemplate
733 **
734 **	@param	subtemplate		The subtemplate
735 **
736 **	@return	The subtemplate's end position if found, or (-1, -1) otherwise
737 */
GetSubtemplateEndPos(const CMapTemplate * subtemplate) const738 Vec2i CMap::GetSubtemplateEndPos(const CMapTemplate *subtemplate) const
739 {
740 	if (!subtemplate) {
741 		return Vec2i(-1, -1);
742 	}
743 
744 	const CMapTemplate *main_template = subtemplate->GetTopMapTemplate();
745 	if (main_template && subtemplate != main_template && main_template->Plane && main_template->World) {
746 		const int z = GetMapLayer(main_template->Plane->Ident, main_template->World->Ident, main_template->SurfaceLayer);
747 		if (z != -1) {
748 			for (size_t i = 0; i < this->MapLayers[z]->SubtemplateAreas.size(); ++i) {
749 				if (subtemplate == std::get<2>(this->MapLayers[z]->SubtemplateAreas[i])) {
750 					return std::get<1>(Map.MapLayers[z]->SubtemplateAreas[i]);
751 				}
752 			}
753 		}
754 	}
755 
756 	return Vec2i(-1, -1);
757 }
758 
759 /**
760 **	@brief	Get the applied map layer of a given subtemplate
761 **
762 **	@param	subtemplate		The subtemplate
763 **
764 **	@return	The subtemplate's map layer if found, or null otherwise
765 */
GetSubtemplateMapLayer(const CMapTemplate * subtemplate) const766 CMapLayer *CMap::GetSubtemplateMapLayer(const CMapTemplate *subtemplate) const
767 {
768 	if (!subtemplate) {
769 		return nullptr;
770 	}
771 
772 	const CMapTemplate *main_template = subtemplate->GetTopMapTemplate();
773 	if (main_template && subtemplate != main_template && main_template->Plane && main_template->World) {
774 		const int z = GetMapLayer(main_template->Plane->Ident, main_template->World->Ident, main_template->SurfaceLayer);
775 		if (z != -1) {
776 			for (size_t i = 0; i < this->MapLayers[z]->SubtemplateAreas.size(); ++i) {
777 				if (subtemplate == std::get<2>(this->MapLayers[z]->SubtemplateAreas[i])) {
778 					return this->MapLayers[z];
779 				}
780 			}
781 		}
782 	}
783 
784 	return nullptr;
785 }
786 
787 /**
788 **	@brief	Get the map layer connectors in a given map template
789 **
790 **	@param	subtemplate		The subtemplate
791 **
792 **	@return	A list of the connector units
793 */
GetMapTemplateLayerConnectors(const CMapTemplate * map_template) const794 std::vector<CUnit *> CMap::GetMapTemplateLayerConnectors(const CMapTemplate *map_template) const
795 {
796 	std::vector<CUnit *> layer_connectors;
797 
798 	if (!map_template) {
799 		return layer_connectors;
800 	}
801 
802 	const CMapTemplate *main_template = map_template->GetTopMapTemplate();
803 	if (main_template && main_template->Plane && main_template->World) {
804 		const bool is_main_template = main_template == map_template;
805 		const int z = GetMapLayer(main_template->Plane->Ident, main_template->World->Ident, main_template->SurfaceLayer);
806 		if (z != -1) {
807 			for (size_t i = 0; i < this->MapLayers[z]->LayerConnectors.size(); ++i) {
808 				CUnit *connector_unit = this->MapLayers[z]->LayerConnectors[i];
809 				const Vec2i unit_pos = connector_unit->GetTileCenterPos();
810 
811 				if (is_main_template && this->IsPointInASubtemplateArea(unit_pos, z)) {
812 					continue;
813 				} else if (!is_main_template && !this->IsPointInASubtemplateArea(unit_pos, z, map_template)) {
814 					continue;
815 				}
816 
817 				layer_connectors.push_back(connector_unit);
818 			}
819 		}
820 	}
821 
822 	return layer_connectors;
823 }
824 
825 /**
826 **	@brief	Get whether a given tile is adjacent to non-subtemplate area tiles
827 **
828 **	@param	pos		The tile's position
829 **	@param	z		The tile's map layer
830 **
831 **	@return	True if the tile is adjacent to a non-subtemplate area tile, or false otherwise
832 */
IsPointAdjacentToNonSubtemplateArea(const Vec2i & pos,const int z) const833 bool CMap::IsPointAdjacentToNonSubtemplateArea(const Vec2i &pos, const int z) const
834 {
835 	for (int x_offset = -1; x_offset <= 1; ++x_offset) {
836 		for (int y_offset = -1; y_offset <= 1; ++y_offset) {
837 			if (x_offset == 0 && y_offset == 0) {
838 				continue;
839 			}
840 
841 			Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
842 
843 			if (Map.Info.IsPointOnMap(adjacent_pos, z) && !this->IsPointInASubtemplateArea(adjacent_pos, z)) {
844 				return true;
845 			}
846 		}
847 	}
848 
849 	return false;
850 }
851 
IsLayerUnderground(int z) const852 bool CMap::IsLayerUnderground(int z) const
853 {
854 	if (GameSettings.Inside) {
855 		return true;
856 	}
857 
858 	if (this->MapLayers[z]->SurfaceLayer > 0) {
859 		return true;
860 	}
861 
862 	return false;
863 }
864 
SetCurrentPlane(CPlane * plane)865 void CMap::SetCurrentPlane(CPlane *plane)
866 {
867 	if (UI.CurrentMapLayer->Plane == plane) {
868 		return;
869 	}
870 
871 	int map_layer = -1;
872 
873 	for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
874 		if (Map.MapLayers[z]->Plane == plane && Map.MapLayers[z]->SurfaceLayer == this->GetCurrentSurfaceLayer()) {
875 			map_layer = z;
876 			break;
877 		}
878 	}
879 
880 	if (map_layer == -1) {
881 		for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
882 			if (Map.MapLayers[z]->Plane == plane) {
883 				map_layer = z;
884 				break;
885 			}
886 		}
887 	}
888 
889 	if (map_layer != -1) {
890 		ChangeCurrentMapLayer(map_layer);
891 	}
892 }
893 
SetCurrentWorld(CWorld * world)894 void CMap::SetCurrentWorld(CWorld *world)
895 {
896 	if (UI.CurrentMapLayer->World == world) {
897 		return;
898 	}
899 
900 	int map_layer = -1;
901 
902 	for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
903 		if (Map.MapLayers[z]->World == world && Map.MapLayers[z]->SurfaceLayer == this->GetCurrentSurfaceLayer()) {
904 			map_layer = z;
905 			break;
906 		}
907 	}
908 
909 	if (map_layer == -1) {
910 		for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
911 			if (Map.MapLayers[z]->World == world) {
912 				map_layer = z;
913 				break;
914 			}
915 		}
916 	}
917 
918 	if (map_layer != -1) {
919 		ChangeCurrentMapLayer(map_layer);
920 	}
921 }
922 
SetCurrentSurfaceLayer(int surface_layer)923 void CMap::SetCurrentSurfaceLayer(int surface_layer)
924 {
925 	if (UI.CurrentMapLayer->SurfaceLayer == surface_layer) {
926 		return;
927 	}
928 
929 	int map_layer = -1;
930 
931 	for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
932 		if (Map.MapLayers[z]->Plane == this->GetCurrentPlane() && Map.MapLayers[z]->World == this->GetCurrentWorld() && Map.MapLayers[z]->SurfaceLayer == surface_layer) {
933 			map_layer = z;
934 			break;
935 		}
936 	}
937 
938 	if (map_layer != -1) {
939 		ChangeCurrentMapLayer(map_layer);
940 	}
941 }
942 
GetCurrentPlane() const943 CPlane *CMap::GetCurrentPlane() const
944 {
945 	if (UI.CurrentMapLayer) {
946 		return UI.CurrentMapLayer->Plane;
947 	} else {
948 		return nullptr;
949 	}
950 }
951 
GetCurrentWorld() const952 CWorld *CMap::GetCurrentWorld() const
953 {
954 	if (UI.CurrentMapLayer) {
955 		return UI.CurrentMapLayer->World;
956 	} else {
957 		return nullptr;
958 	}
959 }
960 
GetCurrentSurfaceLayer() const961 int CMap::GetCurrentSurfaceLayer() const
962 {
963 	if (UI.CurrentMapLayer) {
964 		return UI.CurrentMapLayer->SurfaceLayer;
965 	} else {
966 		return 0;
967 	}
968 }
969 
GetCurrentPixelTileSize() const970 PixelSize CMap::GetCurrentPixelTileSize() const
971 {
972 	if (UI.CurrentMapLayer) {
973 		return UI.CurrentMapLayer->PixelTileSize;
974 	} else {
975 		return PixelSize(32, 32);
976 	}
977 }
978 
GetMapLayerPixelTileSize(int map_layer) const979 PixelSize CMap::GetMapLayerPixelTileSize(int map_layer) const
980 {
981 	if (map_layer >= 0 && map_layer < (int) Map.MapLayers.size()) {
982 		return Map.MapLayers[map_layer]->PixelTileSize;
983 	} else {
984 		return PixelSize(32, 32);
985 	}
986 }
987 //Wyrmgus end
988 
989 /**
990 **  Can move to this point, applying mask.
991 **
992 **  @param pos   map tile position.
993 **  @param mask  Mask for movement to apply.
994 **
995 **  @return      True if could be entered, false otherwise.
996 */
CheckedCanMoveToMask(const Vec2i & pos,int mask,int z)997 bool CheckedCanMoveToMask(const Vec2i &pos, int mask, int z)
998 {
999 	return Map.Info.IsPointOnMap(pos, z) && CanMoveToMask(pos, mask, z);
1000 }
1001 
1002 /**
1003 **  Can a unit of unit-type be placed at this point.
1004 **
1005 **  @param type  unit-type to be checked.
1006 **  @param pos   map tile position.
1007 **
1008 **  @return      True if could be entered, false otherwise.
1009 */
1010 //Wyrmgus start
1011 //bool UnitTypeCanBeAt(const CUnitType &type, const Vec2i &pos)
UnitTypeCanBeAt(const CUnitType & type,const Vec2i & pos,int z)1012 bool UnitTypeCanBeAt(const CUnitType &type, const Vec2i &pos, int z)
1013 //Wyrmgus end
1014 {
1015 	const int mask = type.MovementMask;
1016 	//Wyrmgus start
1017 //	unsigned int index = pos.y * Map.Info.MapWidth;
1018 	unsigned int index = pos.y * Map.Info.MapWidths[z];
1019 	//Wyrmgus end
1020 
1021 	for (int addy = 0; addy < type.TileSize.y; ++addy) {
1022 		for (int addx = 0; addx < type.TileSize.x; ++addx) {
1023 			if (Map.Info.IsPointOnMap(pos.x + addx, pos.y + addy, z) == false
1024 				|| Map.Field(pos.x + addx + index, z)->CheckMask(mask) == true) {
1025 				return false;
1026 			}
1027 		}
1028 		//Wyrmgus start
1029 //		index += Map.Info.MapWidth;
1030 		index += Map.Info.MapWidths[z];
1031 		//Wyrmgus end
1032 	}
1033 	return true;
1034 }
1035 
1036 /**
1037 **  Can a unit be placed to this point.
1038 **
1039 **  @param unit  unit to be checked.
1040 **  @param pos   map tile position.
1041 **
1042 **  @return      True if could be placeded, false otherwise.
1043 */
1044 //Wyrmgus start
1045 //bool UnitCanBeAt(const CUnit &unit, const Vec2i &pos)
UnitCanBeAt(const CUnit & unit,const Vec2i & pos,int z)1046 bool UnitCanBeAt(const CUnit &unit, const Vec2i &pos, int z)
1047 //Wyrmgus end
1048 {
1049 	Assert(unit.Type);
1050 	if (unit.Type->BoolFlag[NONSOLID_INDEX].value) {
1051 		return true;
1052 	}
1053 	//Wyrmgus start
1054 //	return UnitTypeCanBeAt(*unit.Type, pos);
1055 	return UnitTypeCanBeAt(*unit.Type, pos, z);
1056 	//Wyrmgus end
1057 }
1058 
1059 /**
1060 **  Fixes initially the wood and seen tiles.
1061 */
PreprocessMap()1062 void PreprocessMap()
1063 {
1064 	ShowLoadProgress("%s", _("Initializing Map"));
1065 
1066 	//Wyrmgus start
1067 	/*
1068 	for (int ix = 0; ix < Map.Info.MapWidth; ++ix) {
1069 		for (int iy = 0; iy < Map.Info.MapHeight; ++iy) {
1070 			CMapField &mf = *Map.Field(ix, iy);
1071 			mf.playerInfo.SeenTile = mf.getGraphicTile();
1072 		}
1073 	}
1074 	*/
1075 	for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
1076 		for (int ix = 0; ix < Map.Info.MapWidths[z]; ++ix) {
1077 			for (int iy = 0; iy < Map.Info.MapHeights[z]; ++iy) {
1078 				CMapField &mf = *Map.Field(ix, iy, z);
1079 				Map.CalculateTileTransitions(Vec2i(ix, iy), false, z);
1080 				Map.CalculateTileTransitions(Vec2i(ix, iy), true, z);
1081 				Map.CalculateTileLandmass(Vec2i(ix, iy), z);
1082 				Map.CalculateTileOwnership(Vec2i(ix, iy), z);
1083 				Map.CalculateTileTerrainFeature(Vec2i(ix, iy), z);
1084 				mf.UpdateSeenTile();
1085 				UI.Minimap.UpdateXY(Vec2i(ix, iy), z);
1086 				if (mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
1087 					Map.MarkSeenTile(mf, z);
1088 				}
1089 			}
1090 		}
1091 	}
1092 	//Wyrmgus end
1093 	//Wyrmgus start
1094 	/*
1095 	// it is required for fixing the wood that all tiles are marked as seen!
1096 	if (Map.Tileset->TileTypeTable.empty() == false) {
1097 		Vec2i pos;
1098 		for (pos.x = 0; pos.x < Map.Info.MapWidth; ++pos.x) {
1099 			for (pos.y = 0; pos.y < Map.Info.MapHeight; ++pos.y) {
1100 				MapFixWallTile(pos);
1101 				MapFixSeenWallTile(pos);
1102 			}
1103 		}
1104 	}
1105 	*/
1106 	//Wyrmgus end
1107 }
1108 
1109 //Wyrmgus start
GetMapLayer(const std::string & plane_ident,const std::string & world_ident,const int surface_layer)1110 int GetMapLayer(const std::string &plane_ident, const std::string &world_ident, const int surface_layer)
1111 {
1112 	CPlane *plane = CPlane::GetPlane(plane_ident, false);
1113 	CWorld *world = CWorld::GetWorld(world_ident, false);
1114 
1115 	for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
1116 		if (Map.MapLayers[z]->Plane == plane && Map.MapLayers[z]->World == world && Map.MapLayers[z]->SurfaceLayer == surface_layer) {
1117 			return z;
1118 		}
1119 	}
1120 
1121 	return -1;
1122 }
1123 
GetSubtemplateStartX(const std::string & subtemplate_ident)1124 int GetSubtemplateStartX(const std::string &subtemplate_ident)
1125 {
1126 	CMapTemplate *subtemplate = CMapTemplate::GetMapTemplate(subtemplate_ident);
1127 
1128 	if (!subtemplate) {
1129 		return -1;
1130 	}
1131 
1132 	for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
1133 		for (size_t i = 0; i < Map.MapLayers[z]->SubtemplateAreas.size(); ++i) {
1134 			Vec2i min_pos = std::get<0>(Map.MapLayers[z]->SubtemplateAreas[i]);
1135 			if (subtemplate == std::get<2>(Map.MapLayers[z]->SubtemplateAreas[i])) {
1136 				return min_pos.x;
1137 			}
1138 		}
1139 	}
1140 
1141 	return -1;
1142 }
1143 
GetSubtemplateStartY(const std::string & subtemplate_ident)1144 int GetSubtemplateStartY(const std::string &subtemplate_ident)
1145 {
1146 	CMapTemplate *subtemplate = CMapTemplate::GetMapTemplate(subtemplate_ident);
1147 
1148 	if (!subtemplate) {
1149 		return -1;
1150 	}
1151 
1152 	for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
1153 		for (size_t i = 0; i < Map.MapLayers[z]->SubtemplateAreas.size(); ++i) {
1154 			Vec2i min_pos = std::get<0>(Map.MapLayers[z]->SubtemplateAreas[i]);
1155 			if (subtemplate == std::get<2>(Map.MapLayers[z]->SubtemplateAreas[i])) {
1156 				return min_pos.y;
1157 			}
1158 		}
1159 	}
1160 
1161 	return -1;
1162 }
1163 
1164 /**
1165 **	@brief	Change the map layer currently being displayed to the previous one
1166 */
ChangeToPreviousMapLayer()1167 void ChangeToPreviousMapLayer()
1168 {
1169 	if (!UI.PreviousMapLayer) {
1170 		return;
1171 	}
1172 
1173 	ChangeCurrentMapLayer(UI.PreviousMapLayer->ID);
1174 }
1175 
1176 /**
1177 **	@brief	Change the map layer currently being displayed
1178 **
1179 **	@param	z	The map layer
1180 */
ChangeCurrentMapLayer(const int z)1181 void ChangeCurrentMapLayer(const int z)
1182 {
1183 	if (z < 0 || z >= (int) Map.MapLayers.size() || UI.CurrentMapLayer->ID == z) {
1184 		return;
1185 	}
1186 
1187 	Vec2i new_viewport_map_pos(UI.SelectedViewport->MapPos.x * Map.Info.MapWidths[z] / UI.CurrentMapLayer->GetWidth(), UI.SelectedViewport->MapPos.y * Map.Info.MapHeights[z] / UI.CurrentMapLayer->GetHeight());
1188 
1189 	UI.PreviousMapLayer = UI.CurrentMapLayer;
1190 	UI.CurrentMapLayer = Map.MapLayers[z];
1191 	UI.Minimap.UpdateCache = true;
1192 	UI.SelectedViewport->Set(new_viewport_map_pos, Map.GetCurrentPixelTileSize() / 2);
1193 	UpdateSurfaceLayerButtons();
1194 }
1195 
1196 /**
1197 **	@brief	Set the current time of day for a particular map layer
1198 **
1199 **	@param	time_of_day_ident	The time of day's string identifier
1200 **	@param	z					The map layer
1201 */
SetTimeOfDay(const std::string & time_of_day_ident,int z)1202 void SetTimeOfDay(const std::string &time_of_day_ident, int z)
1203 {
1204 	if (time_of_day_ident.empty()) {
1205 		Map.MapLayers[z]->SetTimeOfDay(nullptr);
1206 		Map.MapLayers[z]->RemainingTimeOfDayHours = 0;
1207 	} else {
1208 		CTimeOfDaySchedule *schedule = Map.MapLayers[z]->TimeOfDaySchedule;
1209 		if (schedule) {
1210 			for (size_t i = 0; i < schedule->ScheduledTimesOfDay.size(); ++i) {
1211 				CScheduledTimeOfDay *time_of_day = schedule->ScheduledTimesOfDay[i];
1212 				if (time_of_day->TimeOfDay->Ident == time_of_day_ident)  {
1213 					Map.MapLayers[z]->SetTimeOfDay(time_of_day);
1214 					Map.MapLayers[z]->RemainingTimeOfDayHours = time_of_day->GetHours(Map.MapLayers[z]->GetSeason());
1215 					break;
1216 				}
1217 			}
1218 		}
1219 	}
1220 }
1221 
1222 /**
1223 **	@brief	Set the time of day schedule for a particular map layer
1224 **
1225 **	@param	time_of_day_schedule_ident	The time of day schedule's string identifier
1226 **	@param	z							The map layer
1227 */
SetTimeOfDaySchedule(const std::string & time_of_day_schedule_ident,int z)1228 void SetTimeOfDaySchedule(const std::string &time_of_day_schedule_ident, int z)
1229 {
1230 	if (time_of_day_schedule_ident.empty()) {
1231 		Map.MapLayers[z]->TimeOfDaySchedule = nullptr;
1232 		Map.MapLayers[z]->SetTimeOfDay(nullptr);
1233 		Map.MapLayers[z]->RemainingTimeOfDayHours = 0;
1234 	} else {
1235 		CTimeOfDaySchedule *schedule = CTimeOfDaySchedule::GetTimeOfDaySchedule(time_of_day_schedule_ident);
1236 		if (schedule) {
1237 			Map.MapLayers[z]->TimeOfDaySchedule = schedule;
1238 			Map.MapLayers[z]->SetTimeOfDay(schedule->ScheduledTimesOfDay.front());
1239 			Map.MapLayers[z]->RemainingTimeOfDayHours = Map.MapLayers[z]->TimeOfDay->GetHours(Map.MapLayers[z]->GetSeason());
1240 		}
1241 	}
1242 }
1243 
1244 /**
1245 **	@brief	Set the current season for a particular map layer
1246 **
1247 **	@param	season_ident		The season's string identifier
1248 **	@param	z					The map layer
1249 */
SetSeason(const std::string & season_ident,int z)1250 void SetSeason(const std::string &season_ident, int z)
1251 {
1252 	if (season_ident.empty()) {
1253 		Map.MapLayers[z]->SetSeason(nullptr);
1254 		Map.MapLayers[z]->RemainingSeasonHours = 0;
1255 	} else {
1256 		CSeasonSchedule *schedule = Map.MapLayers[z]->SeasonSchedule;
1257 		if (schedule) {
1258 			for (size_t i = 0; i < schedule->ScheduledSeasons.size(); ++i) {
1259 				CScheduledSeason *season = schedule->ScheduledSeasons[i];
1260 				if (season->Season->Ident == season_ident)  {
1261 					Map.MapLayers[z]->SetSeason(season);
1262 					Map.MapLayers[z]->RemainingSeasonHours = season->Hours;
1263 					break;
1264 				}
1265 			}
1266 		}
1267 	}
1268 }
1269 
1270 /**
1271 **	@brief	Set the season schedule for a particular map layer
1272 **
1273 **	@param	season_schedule_ident		The season schedule's string identifier
1274 **	@param	z							The map layer
1275 */
SetSeasonSchedule(const std::string & season_schedule_ident,int z)1276 void SetSeasonSchedule(const std::string &season_schedule_ident, int z)
1277 {
1278 	if (season_schedule_ident.empty()) {
1279 		Map.MapLayers[z]->SeasonSchedule = nullptr;
1280 		Map.MapLayers[z]->SetSeason(nullptr);
1281 		Map.MapLayers[z]->RemainingSeasonHours = 0;
1282 	} else {
1283 		CSeasonSchedule *schedule = CSeasonSchedule::GetSeasonSchedule(season_schedule_ident);
1284 		if (schedule) {
1285 			Map.MapLayers[z]->SeasonSchedule = schedule;
1286 			Map.MapLayers[z]->SetSeason(schedule->ScheduledSeasons.front());
1287 			Map.MapLayers[z]->RemainingSeasonHours = Map.MapLayers[z]->Season->Hours;
1288 		}
1289 	}
1290 }
1291 //Wyrmgus end
1292 
1293 /**
1294 **	@brief	Get whether a given coordinate is a valid point on the map
1295 **
1296 **	@param	x	The x coordinate
1297 **	@param	y	The y coordinate
1298 **	@param	z	The map layer
1299 **
1300 **	@return	True if the coordinate is valid, false otherwise
1301 */
IsPointOnMap(const int x,const int y,const int z) const1302 bool CMapInfo::IsPointOnMap(const int x, const int y, const int z) const
1303 {
1304 	return (z >= 0 && z < (int) MapWidths.size() && z < (int) MapHeights.size() && x >= 0 && y >= 0 && x < MapWidths[z] && y < MapHeights[z]);
1305 }
1306 
1307 /**
1308 **	@brief	Get whether a given coordinate is a valid point on the map
1309 **
1310 **	@param	pos	The coordinate position
1311 **	@param	z	The map layer
1312 **
1313 **	@return	True if the coordinate is valid, false otherwise
1314 */
IsPointOnMap(const Vec2i & pos,const int z) const1315 bool CMapInfo::IsPointOnMap(const Vec2i &pos, const int z) const
1316 {
1317 	return IsPointOnMap(pos.x, pos.y, z);
1318 }
1319 
1320 /**
1321 **	@brief	Get whether a given coordinate is a valid point on the map
1322 **
1323 **	@param	x			The x coordinate
1324 **	@param	y			The y coordinate
1325 **	@param	map_layer	The map layer
1326 **
1327 **	@return	True if the coordinate is valid, false otherwise
1328 */
IsPointOnMap(const int x,const int y,const CMapLayer * map_layer) const1329 bool CMapInfo::IsPointOnMap(const int x, const int y, const CMapLayer *map_layer) const
1330 {
1331 	return (map_layer && x >= 0 && y >= 0 && x < map_layer->GetWidth() && y < map_layer->GetHeight());
1332 }
1333 
1334 /**
1335 **	@brief	Get whether a given coordinate is a valid point on the map
1336 **
1337 **	@param	pos			The coordinate position
1338 **	@param	map_layer	The map layer
1339 **
1340 **	@return	True if the coordinate is valid, false otherwise
1341 */
IsPointOnMap(const Vec2i & pos,const CMapLayer * map_layer) const1342 bool CMapInfo::IsPointOnMap(const Vec2i &pos, const CMapLayer *map_layer) const
1343 {
1344 	return IsPointOnMap(pos.x, pos.y, map_layer);
1345 }
1346 
1347 /**
1348 **	@brief	Clear CMapInfo
1349 */
Clear()1350 void CMapInfo::Clear()
1351 {
1352 	this->Description.clear();
1353 	this->Filename.clear();
1354 	this->MapWidth = this->MapHeight = 0;
1355 	//Wyrmgus start
1356 	this->MapWidths.clear();
1357 	this->MapHeights.clear();
1358 	//Wyrmgus end
1359 	memset(this->PlayerSide, 0, sizeof(this->PlayerSide));
1360 	memset(this->PlayerType, 0, sizeof(this->PlayerType));
1361 	this->MapUID = 0;
1362 }
1363 
CMap()1364 CMap::CMap() : NoFogOfWar(false), TileGraphic(nullptr), Landmasses(0), BorderTerrain(nullptr)
1365 {
1366 	Tileset = new CTileset;
1367 }
1368 
~CMap()1369 CMap::~CMap()
1370 {
1371 	delete Tileset;
1372 }
1373 
getIndex(int x,int y,int z) const1374 unsigned int CMap::getIndex(int x, int y, int z) const
1375 {
1376 	return x + y * this->Info.MapWidths[z];
1377 }
1378 
getIndex(const Vec2i & pos,int z) const1379 unsigned int CMap::getIndex(const Vec2i &pos, int z) const
1380 {
1381 	return getIndex(pos.x, pos.y, z);
1382 }
1383 
1384 /**
1385 **	@brief	Get the map field at a given location
1386 **
1387 **	@param	index	The index of the map field
1388 **	@param	z		The map layer of the map field
1389 **
1390 **	@return	The map field
1391 */
Field(const unsigned int index,const int z) const1392 CMapField *CMap::Field(const unsigned int index, const int z) const
1393 {
1394 	return this->MapLayers[z]->Field(index);
1395 }
1396 
1397 /**
1398 **	@brief	Get the map field at a given location
1399 **
1400 **	@param	x	The x coordinate of the map field
1401 **	@param	y	The y coordinate of the map field
1402 **	@param	z	The map layer of the map field
1403 **
1404 **	@return	The map field
1405 */
Field(const int x,const int y,const int z) const1406 CMapField *CMap::Field(const int x, const int y, const int z) const
1407 {
1408 	return this->MapLayers[z]->Field(x, y);
1409 }
1410 
1411 /**
1412 **	@brief	Allocate and initialize map table
1413 */
Create()1414 void CMap::Create()
1415 {
1416 	Assert(this->MapLayers.size() == 0);
1417 
1418 	CMapLayer *map_layer = new CMapLayer(this->Info.MapWidth, this->Info.MapHeight);
1419 	map_layer->ID = this->MapLayers.size();
1420 	this->MapLayers.push_back(map_layer);
1421 	this->Info.MapWidths.push_back(this->Info.MapWidth);
1422 	this->Info.MapHeights.push_back(this->Info.MapHeight);
1423 
1424 	if (Editor.Running == EditorNotRunning) {
1425 		map_layer->SeasonSchedule = CSeasonSchedule::DefaultSeasonSchedule;
1426 		map_layer->SetSeasonByHours(CDate::CurrentTotalHours);
1427 
1428 		if (!GameSettings.Inside && !GameSettings.NoTimeOfDay) {
1429 			map_layer->TimeOfDaySchedule = CTimeOfDaySchedule::DefaultTimeOfDaySchedule;
1430 			map_layer->SetTimeOfDayByHours(CDate::CurrentTotalHours);
1431 		} else {
1432 			map_layer->TimeOfDaySchedule = nullptr;
1433 			map_layer->SetTimeOfDay(nullptr); // make indoors have no time of day setting until it is possible to make light sources change their surrounding "time of day" // indoors it is always dark (maybe would be better to allow a special setting to have bright indoor places?
1434 		}
1435 	}
1436 }
1437 
1438 /**
1439 **  Initialize the fog of war.
1440 **  Build tables, setup functions.
1441 */
Init()1442 void CMap::Init()
1443 {
1444 	for (std::map<PixelSize, CGraphic *>::iterator iterator = this->FogGraphics.begin(); iterator != this->FogGraphics.end(); ++iterator) {
1445 		InitFogOfWar(iterator->first);
1446 	}
1447 }
1448 
1449 /**
1450 **  Cleanup the map module.
1451 */
Clean()1452 void CMap::Clean()
1453 {
1454 	UI.CurrentMapLayer = nullptr;
1455 	UI.PreviousMapLayer = nullptr;
1456 	this->Landmasses = 0;
1457 
1458 	//Wyrmgus start
1459 	this->ClearMapLayers();
1460 	this->BorderLandmasses.clear();
1461 	this->SiteUnits.clear();
1462 	//Wyrmgus end
1463 
1464 	// Tileset freed by Tileset?
1465 
1466 	this->Info.Clear();
1467 	this->NoFogOfWar = false;
1468 	this->Tileset->clear();
1469 	this->TileModelsFileName.clear();
1470 	CGraphic::Free(this->TileGraphic);
1471 	this->TileGraphic = nullptr;
1472 
1473 	FlagRevealMap = 0;
1474 	ReplayRevealMap = 0;
1475 
1476 	UI.Minimap.Destroy();
1477 }
1478 
ClearMapLayers()1479 void CMap::ClearMapLayers()
1480 {
1481 	for (size_t z = 0; z < this->MapLayers.size(); ++z) {
1482 		delete this->MapLayers[z];
1483 	}
1484 	this->MapLayers.clear();
1485 }
1486 
1487 /**
1488 ** Save the complete map.
1489 **
1490 ** @param file Output file.
1491 */
Save(CFile & file) const1492 void CMap::Save(CFile &file) const
1493 {
1494 	file.printf("\n--- -----------------------------------------\n");
1495 	file.printf("--- MODULE: map\n");
1496 	file.printf("LoadTileModels(\"%s\")\n\n", this->TileModelsFileName.c_str());
1497 	file.printf("StratagusMap(\n");
1498 	file.printf("  \"version\", \"%s\",\n", VERSION);
1499 	file.printf("  \"description\", \"%s\",\n", this->Info.Description.c_str());
1500 	file.printf("  \"the-map\", {\n");
1501 	file.printf("  \"size\", {%d, %d},\n", this->Info.MapWidth, this->Info.MapHeight);
1502 	file.printf("  \"%s\",\n", this->NoFogOfWar ? "no-fog-of-war" : "fog-of-war");
1503 	file.printf("  \"filename\", \"%s\",\n", this->Info.Filename.c_str());
1504 	//Wyrmgus start
1505 	file.printf("  \"extra-map-layers\", {\n");
1506 	for (size_t z = 1; z < this->MapLayers.size(); ++z) {
1507 		file.printf("  {%d, %d},\n", this->Info.MapWidths[z], this->Info.MapHeights[z]);
1508 	}
1509 	file.printf("  },\n");
1510 	file.printf("  \"time-of-day\", {\n");
1511 	for (size_t z = 0; z < this->MapLayers.size(); ++z) {
1512 		file.printf("  {\"%s\", %d, %d},\n", this->MapLayers[z]->TimeOfDaySchedule ? this->MapLayers[z]->TimeOfDaySchedule->Ident.c_str() : "", this->MapLayers[z]->TimeOfDay ? this->MapLayers[z]->TimeOfDay->ID : 0, this->MapLayers[z]->RemainingTimeOfDayHours);
1513 	}
1514 	file.printf("  },\n");
1515 	file.printf("  \"season\", {\n");
1516 	for (size_t z = 0; z < this->MapLayers.size(); ++z) {
1517 		file.printf("  {\"%s\", %d, %d},\n", this->MapLayers[z]->SeasonSchedule ? this->MapLayers[z]->SeasonSchedule->Ident.c_str() : "", this->MapLayers[z]->Season ? this->MapLayers[z]->Season->ID : 0, this->MapLayers[z]->RemainingSeasonHours);
1518 	}
1519 	file.printf("  },\n");
1520 	file.printf("  \"pixel-tile-size\", {\n");
1521 	for (size_t z = 0; z < this->MapLayers.size(); ++z) {
1522 		file.printf("  {%d, %d},\n", this->MapLayers[z]->PixelTileSize.x, this->MapLayers[z]->PixelTileSize.y);
1523 	}
1524 	file.printf("  },\n");
1525 	file.printf("  \"layer-references\", {\n");
1526 	for (size_t z = 0; z < this->MapLayers.size(); ++z) {
1527 		file.printf("  {\"%s\", \"%s\", %d},\n", this->MapLayers[z]->Plane ? this->MapLayers[z]->Plane->Ident.c_str() : "", this->MapLayers[z]->World ? this->MapLayers[z]->World->Ident.c_str() : "", this->MapLayers[z]->SurfaceLayer);
1528 	}
1529 	file.printf("  },\n");
1530 	file.printf("  \"landmasses\", {\n");
1531 	for (int i = 1; i <= this->Landmasses; ++i) {
1532 		file.printf("  {");
1533 		for (size_t j = 0; j < this->BorderLandmasses[i].size(); ++j) {
1534 			file.printf("%d, ", this->BorderLandmasses[i][j]);
1535 		}
1536 		file.printf("},\n");
1537 	}
1538 	file.printf("  },\n");
1539 	//Wyrmgus end
1540 
1541 	file.printf("  \"map-fields\", {\n");
1542 	//Wyrmgus start
1543 	/*
1544 	for (int h = 0; h < this->Info.MapHeight; ++h) {
1545 		file.printf("  -- %d\n", h);
1546 		for (int w = 0; w < this->Info.MapWidth; ++w) {
1547 			const CMapField &mf = *this->Field(w, h);
1548 
1549 			mf.Save(file);
1550 			if (w & 1) {
1551 				file.printf(",\n");
1552 			} else {
1553 				file.printf(", ");
1554 			}
1555 		}
1556 	}
1557 	*/
1558 	for (size_t z = 0; z < this->MapLayers.size(); ++z) {
1559 		file.printf("  {\n");
1560 		for (int h = 0; h < this->Info.MapHeights[z]; ++h) {
1561 			file.printf("  -- %d\n", h);
1562 			for (int w = 0; w < this->Info.MapWidths[z]; ++w) {
1563 				const CMapField &mf = *this->Field(w, h, z);
1564 
1565 				mf.Save(file);
1566 				if (w & 1) {
1567 					file.printf(",\n");
1568 				} else {
1569 					file.printf(", ");
1570 				}
1571 			}
1572 		}
1573 		file.printf("  },\n");
1574 	}
1575 	//Wyrmgus end
1576 	file.printf("}})\n");
1577 }
1578 
1579 /*----------------------------------------------------------------------------
1580 -- Map Tile Update Functions
1581 ----------------------------------------------------------------------------*/
1582 
1583 /**
1584 **  Correct the seen wood field, depending on the surrounding.
1585 **
1586 **  @param type  type of tile to update
1587 **  @param seen  1 if updating seen value, 0 for real
1588 **  @param pos   Map tile-position.
1589 */
1590 //Wyrmgus start
1591 /*
1592 void CMap::FixTile(unsigned short type, int seen, const Vec2i &pos)
1593 {
1594 	Assert(type == MapFieldForest || type == MapFieldRocks);
1595 
1596 	//  Outside of map or no wood.
1597 	if (!Info.IsPointOnMap(pos)) {
1598 		return;
1599 	}
1600 	unsigned int index = getIndex(pos);
1601 	CMapField &mf = *this->Field(index);
1602 
1603 	if (!((type == MapFieldForest && Tileset->isAWoodTile(mf.playerInfo.SeenTile))
1604 		  || (type == MapFieldRocks && Tileset->isARockTile(mf.playerInfo.SeenTile)))) {
1605 		if (seen) {
1606 			return;
1607 		}
1608 	}
1609 
1610 	if (!seen && !(mf.getFlag() & type)) {
1611 		return;
1612 	}
1613 
1614 	// Select Table to lookup
1615 	int removedtile;
1616 	int flags;
1617 	if (type == MapFieldForest) {
1618 		removedtile = this->Tileset->getRemovedTreeTile();
1619 		flags = (MapFieldForest | MapFieldUnpassable);
1620 	} else { // (type == MapFieldRocks)
1621 		removedtile = this->Tileset->getRemovedRockTile();
1622 		flags = (MapFieldRocks | MapFieldUnpassable);
1623 	}
1624 	//  Find out what each tile has with respect to wood, or grass.
1625 	int ttup;
1626 	int ttdown;
1627 	int ttleft;
1628 	int ttright;
1629 
1630 	if (pos.y - 1 < 0) {
1631 		ttup = -1; //Assign trees in all directions
1632 	} else {
1633 		const CMapField &new_mf = *(&mf - this->Info.MapWidth);
1634 		ttup = seen ? new_mf.playerInfo.SeenTile : new_mf.getGraphicTile();
1635 	}
1636 	if (pos.x + 1 >= this->Info.MapWidth) {
1637 		ttright = -1; //Assign trees in all directions
1638 	} else {
1639 		const CMapField &new_mf = *(&mf + 1);
1640 		ttright = seen ? new_mf.playerInfo.SeenTile : new_mf.getGraphicTile();
1641 	}
1642 	if (pos.y + 1 >= this->Info.MapHeight) {
1643 		ttdown = -1; //Assign trees in all directions
1644 	} else {
1645 		const CMapField &new_mf = *(&mf + this->Info.MapWidth);
1646 		ttdown = seen ? new_mf.playerInfo.SeenTile : new_mf.getGraphicTile();
1647 	}
1648 	if (pos.x - 1 < 0) {
1649 		ttleft = -1; //Assign trees in all directions
1650 	} else {
1651 		const CMapField &new_mf = *(&mf - 1);
1652 		ttleft = seen ? new_mf.playerInfo.SeenTile : new_mf.getGraphicTile();
1653 	}
1654 	int tile = this->Tileset->getTileBySurrounding(type, ttup, ttright, ttdown, ttleft);
1655 
1656 	//Update seen tile.
1657 	if (tile == -1) { // No valid wood remove it.
1658 		if (seen) {
1659 			mf.playerInfo.SeenTile = removedtile;
1660 			this->FixNeighbors(type, seen, pos);
1661 		} else {
1662 			mf.setGraphicTile(removedtile);
1663 			mf.Flags &= ~flags;
1664 			mf.Value = 0;
1665 			UI.Minimap.UpdateXY(pos);
1666 		}
1667 	} else if (seen && this->Tileset->isEquivalentTile(tile, mf.playerInfo.SeenTile)) { //Same Type
1668 		return;
1669 	} else {
1670 		if (seen) {
1671 			mf.playerInfo.SeenTile = tile;
1672 		} else {
1673 			mf.setGraphicTile(tile);
1674 		}
1675 	}
1676 
1677 	//maybe isExplored
1678 	if (mf.playerInfo.IsExplored(*ThisPlayer)) {
1679 		UI.Minimap.UpdateSeenXY(pos);
1680 		if (!seen) {
1681 			MarkSeenTile(mf);
1682 		}
1683 	}
1684 }
1685 */
1686 //Wyrmgus end
1687 
1688 /**
1689 **  Correct the surrounding fields.
1690 **
1691 **  @param type  Tiletype of tile to adjust
1692 **  @param seen  1 if updating seen value, 0 for real
1693 **  @param pos   Map tile-position.
1694 */
1695 //Wyrmgus start
1696 /*
1697 void CMap::FixNeighbors(unsigned short type, int seen, const Vec2i &pos)
1698 {
1699 	const Vec2i offset[] = {Vec2i(1, 0), Vec2i(-1, 0), Vec2i(0, 1), Vec2i(0, -1),
1700 							Vec2i(-1, -1), Vec2i(-1, 1), Vec2i(1, -1), Vec2i(1, 1)
1701 						   };
1702 
1703 	for (unsigned int i = 0; i < sizeof(offset) / sizeof(*offset); ++i) {
1704 		FixTile(type, seen, pos + offset[i]);
1705 	}
1706 }
1707 */
1708 //Wyrmgus end
1709 
1710 //Wyrmgus start
SetTileTerrain(const Vec2i & pos,CTerrainType * terrain,int z)1711 void CMap::SetTileTerrain(const Vec2i &pos, CTerrainType *terrain, int z)
1712 {
1713 	if (!terrain) {
1714 		return;
1715 	}
1716 
1717 	CMapField &mf = *this->Field(pos, z);
1718 
1719 	CTerrainType *old_terrain = this->GetTileTerrain(pos, terrain->Overlay, z);
1720 
1721 	if (terrain->Overlay) {
1722 		if (mf.OverlayTerrain == terrain) {
1723 			return;
1724 		}
1725 	} else {
1726 		if (mf.Terrain == terrain) {
1727 			return;
1728 		}
1729 	}
1730 
1731 	mf.SetTerrain(terrain);
1732 
1733 	if (terrain->Overlay) {
1734 		//remove decorations if the overlay terrain has changed
1735 		std::vector<CUnit *> table;
1736 		Select(pos, pos, table, z);
1737 		for (size_t i = 0; i != table.size(); ++i) {
1738 			if (table[i] && table[i]->IsAlive() && table[i]->Type->UnitType == UnitTypeLand && table[i]->Type->BoolFlag[DECORATION_INDEX].value) {
1739 				if (Editor.Running == EditorNotRunning) {
1740 					LetUnitDie(*table[i]);
1741 				} else {
1742 					EditorActionRemoveUnit(*table[i], false);
1743 				}
1744 			}
1745 		}
1746 	}
1747 
1748 	this->CalculateTileTransitions(pos, false, z); //recalculate both, since one may have changed the other
1749 	this->CalculateTileTransitions(pos, true, z);
1750 	this->CalculateTileTerrainFeature(pos, z);
1751 
1752 	if (mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
1753 		MarkSeenTile(mf, z);
1754 	}
1755 	UI.Minimap.UpdateXY(pos, z);
1756 
1757 	for (int x_offset = -1; x_offset <= 1; ++x_offset) {
1758 		for (int y_offset = -1; y_offset <= 1; ++y_offset) {
1759 			if (x_offset != 0 || y_offset != 0) {
1760 				Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
1761 				if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
1762 					CMapField &adjacent_mf = *this->Field(adjacent_pos, z);
1763 
1764 					if (terrain->Overlay && adjacent_mf.OverlayTerrain != terrain && Editor.Running == EditorNotRunning) {
1765 						continue;
1766 					}
1767 
1768 					this->CalculateTileTransitions(adjacent_pos, false, z);
1769 					this->CalculateTileTransitions(adjacent_pos, true, z);
1770 
1771 					if (adjacent_mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
1772 						MarkSeenTile(adjacent_mf, z);
1773 					}
1774 					UI.Minimap.UpdateXY(adjacent_pos, z);
1775 				}
1776 			}
1777 		}
1778 	}
1779 
1780 	if (terrain->Overlay) {
1781 		if ((terrain->Flags & MapFieldUnpassable) || (old_terrain && (old_terrain->Flags & MapFieldUnpassable))) {
1782 			Map.CalculateTileOwnership(pos, z);
1783 
1784 			for (int x_offset = -16; x_offset <= 16; ++x_offset) {
1785 				for (int y_offset = -16; y_offset <= 16; ++y_offset) {
1786 					if (x_offset != 0 || y_offset != 0) {
1787 						Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
1788 						if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
1789 							Map.CalculateTileOwnership(adjacent_pos, z);
1790 						}
1791 					}
1792 				}
1793 			}
1794 		}
1795 	}
1796 }
1797 
1798 //Wyrmgus start
1799 //void CMap::RemoveTileOverlayTerrain(const Vec2i &pos)
RemoveTileOverlayTerrain(const Vec2i & pos,int z)1800 void CMap::RemoveTileOverlayTerrain(const Vec2i &pos, int z)
1801 //Wyrmgus end
1802 {
1803 	CMapField &mf = *this->Field(pos, z);
1804 
1805 	if (!mf.OverlayTerrain) {
1806 		return;
1807 	}
1808 
1809 	CTerrainType *old_terrain = mf.OverlayTerrain;
1810 
1811 	mf.RemoveOverlayTerrain();
1812 
1813 	this->CalculateTileTransitions(pos, true, z);
1814 	this->CalculateTileTerrainFeature(pos, z);
1815 
1816 	if (mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
1817 		MarkSeenTile(mf, z);
1818 	}
1819 	UI.Minimap.UpdateXY(pos, z);
1820 
1821 	for (int x_offset = -1; x_offset <= 1; ++x_offset) {
1822 		for (int y_offset = -1; y_offset <= 1; ++y_offset) {
1823 			if (x_offset != 0 || y_offset != 0) {
1824 				Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
1825 				if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
1826 					CMapField &adjacent_mf = *this->Field(adjacent_pos, z);
1827 
1828 					this->CalculateTileTransitions(adjacent_pos, true, z);
1829 
1830 					if (adjacent_mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
1831 						MarkSeenTile(adjacent_mf, z);
1832 					}
1833 					UI.Minimap.UpdateXY(adjacent_pos, z);
1834 				}
1835 			}
1836 		}
1837 	}
1838 
1839 	if (old_terrain->Flags & MapFieldUnpassable) {
1840 		Map.CalculateTileOwnership(pos, z);
1841 
1842 		for (int x_offset = -16; x_offset <= 16; ++x_offset) {
1843 			for (int y_offset = -16; y_offset <= 16; ++y_offset) {
1844 				if (x_offset != 0 || y_offset != 0) {
1845 					Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
1846 					if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
1847 						Map.CalculateTileOwnership(adjacent_pos, z);
1848 					}
1849 				}
1850 			}
1851 		}
1852 	}
1853 }
1854 
SetOverlayTerrainDestroyed(const Vec2i & pos,bool destroyed,int z)1855 void CMap::SetOverlayTerrainDestroyed(const Vec2i &pos, bool destroyed, int z)
1856 {
1857 	CMapLayer *map_layer = this->MapLayers[z];
1858 
1859 	if (!map_layer) {
1860 		return;
1861 	}
1862 
1863 	CMapField &mf = *map_layer->Field(pos);
1864 
1865 	if (!mf.OverlayTerrain || mf.OverlayTerrainDestroyed == destroyed) {
1866 		return;
1867 	}
1868 
1869 	mf.SetOverlayTerrainDestroyed(destroyed);
1870 
1871 	if (destroyed) {
1872 		if (mf.OverlayTerrain->Flags & MapFieldForest) {
1873 			mf.Flags &= ~(MapFieldForest | MapFieldUnpassable);
1874 			mf.Flags |= MapFieldStumps;
1875 			map_layer->DestroyedForestTiles.push_back(pos);
1876 		} else if (mf.OverlayTerrain->Flags & MapFieldRocks) {
1877 			mf.Flags &= ~(MapFieldRocks | MapFieldUnpassable);
1878 			mf.Flags |= MapFieldGravel;
1879 		} else if (mf.OverlayTerrain->Flags & MapFieldWall) {
1880 			mf.Flags &= ~(MapFieldWall | MapFieldUnpassable);
1881 			if (GameSettings.Inside) {
1882 				mf.Flags &= ~(MapFieldAirUnpassable);
1883 			}
1884 			mf.Flags |= MapFieldGravel;
1885 		}
1886 		mf.Value = 0;
1887 	} else {
1888 		if (mf.Flags & MapFieldStumps) { //if is a cleared tree tile regrowing trees
1889 			mf.Flags &= ~(MapFieldStumps);
1890 			mf.Flags |= MapFieldForest | MapFieldUnpassable;
1891 			mf.Value = CResource::Resources[WoodCost]->DefaultAmount;
1892 		}
1893 	}
1894 
1895 	this->CalculateTileTransitions(pos, true, z);
1896 
1897 	if (mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
1898 		MarkSeenTile(mf, z);
1899 	}
1900 	UI.Minimap.UpdateXY(pos, z);
1901 
1902 	for (int x_offset = -1; x_offset <= 1; ++x_offset) {
1903 		for (int y_offset = -1; y_offset <= 1; ++y_offset) {
1904 			if (x_offset != 0 || y_offset != 0) {
1905 				Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
1906 				if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
1907 					CMapField &adjacent_mf = *this->Field(adjacent_pos, z);
1908 
1909 					if (adjacent_mf.OverlayTerrain != mf.OverlayTerrain) {
1910 						continue;
1911 					}
1912 
1913 					this->CalculateTileTransitions(adjacent_pos, true, z);
1914 
1915 					if (adjacent_mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
1916 						MarkSeenTile(adjacent_mf, z);
1917 					}
1918 					UI.Minimap.UpdateXY(adjacent_pos, z);
1919 				}
1920 			}
1921 		}
1922 	}
1923 
1924 	if (mf.OverlayTerrain->Flags & MapFieldUnpassable) {
1925 		Map.CalculateTileOwnership(pos, z);
1926 
1927 		for (int x_offset = -16; x_offset <= 16; ++x_offset) {
1928 			for (int y_offset = -16; y_offset <= 16; ++y_offset) {
1929 				if (x_offset != 0 || y_offset != 0) {
1930 					Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
1931 					if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
1932 						Map.CalculateTileOwnership(adjacent_pos, z);
1933 					}
1934 				}
1935 			}
1936 		}
1937 	}
1938 }
1939 
SetOverlayTerrainDamaged(const Vec2i & pos,bool damaged,int z)1940 void CMap::SetOverlayTerrainDamaged(const Vec2i &pos, bool damaged, int z)
1941 {
1942 	CMapField &mf = *this->Field(pos, z);
1943 
1944 	if (!mf.OverlayTerrain || mf.OverlayTerrainDamaged == damaged) {
1945 		return;
1946 	}
1947 
1948 	mf.SetOverlayTerrainDamaged(damaged);
1949 
1950 	this->CalculateTileTransitions(pos, true, z);
1951 
1952 	if (mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
1953 		MarkSeenTile(mf, z);
1954 	}
1955 	UI.Minimap.UpdateXY(pos, z);
1956 }
1957 
GetTransitionType(std::vector<int> & adjacent_directions,bool allow_single=false)1958 static int GetTransitionType(std::vector<int> &adjacent_directions, bool allow_single = false)
1959 {
1960 	if (adjacent_directions.size() == 0) {
1961 		return -1;
1962 	}
1963 
1964 	int transition_type = -1;
1965 
1966 	if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end()) {
1967 		transition_type = SingleTransitionType;
1968 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end()) {
1969 		transition_type = NorthSingleTransitionType;
1970 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end()) {
1971 		transition_type = SouthSingleTransitionType;
1972 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
1973 		transition_type = WestSingleTransitionType;
1974 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end()) {
1975 		transition_type = EastSingleTransitionType;
1976 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
1977 		transition_type = NorthSouthTransitionType;
1978 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end()) {
1979 		transition_type = WestEastTransitionType;
1980 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end()) {
1981 		transition_type = NorthSouthwestInnerSoutheastInnerTransitionType;
1982 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end()) {
1983 		transition_type = NorthSouthwestInnerTransitionType;
1984 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end()) {
1985 		transition_type = NorthSoutheastInnerTransitionType;
1986 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end()) {
1987 		transition_type = SouthNorthwestInnerNortheastInnerTransitionType;
1988 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end()) {
1989 		transition_type = SouthNorthwestInnerTransitionType;
1990 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end()) {
1991 		transition_type = SouthNortheastInnerTransitionType;
1992 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end()) {
1993 		transition_type = WestNortheastInnerSoutheastInnerTransitionType;
1994 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end()) {
1995 		transition_type = WestNortheastInnerTransitionType;
1996 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end()) {
1997 		transition_type = WestSoutheastInnerTransitionType;
1998 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end()) {
1999 		transition_type = EastNorthwestInnerSouthwestInnerTransitionType;
2000 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end()) {
2001 		transition_type = EastNorthwestInnerTransitionType;
2002 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end()) {
2003 		transition_type = EastSouthwestInnerTransitionType;
2004 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end()) {
2005 		transition_type = NorthwestOuterSoutheastInnerTransitionType;
2006 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end()) {
2007 		transition_type = NortheastOuterSouthwestInnerTransitionType;
2008 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end()) {
2009 		transition_type = SouthwestOuterNortheastInnerTransitionType;
2010 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end()) {
2011 		transition_type = SoutheastOuterNorthwestInnerTransitionType;
2012 	} else if (std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2013 		transition_type = NorthTransitionType;
2014 	} else if (std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2015 		transition_type = SouthTransitionType;
2016 	} else if (std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end()) {
2017 		transition_type = WestTransitionType;
2018 	} else if (std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end()) {
2019 		transition_type = EastTransitionType;
2020 	} else if ((std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() || std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end()) && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) == adjacent_directions.end()) {
2021 		transition_type = NorthwestOuterTransitionType;
2022 	} else if ((std::find(adjacent_directions.begin(), adjacent_directions.end(), North) != adjacent_directions.end() || std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end()) && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) == adjacent_directions.end()) {
2023 		transition_type = NortheastOuterTransitionType;
2024 	} else if ((std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() || std::find(adjacent_directions.begin(), adjacent_directions.end(), West) != adjacent_directions.end()) && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) == adjacent_directions.end()) {
2025 		transition_type = SouthwestOuterTransitionType;
2026 	} else if ((std::find(adjacent_directions.begin(), adjacent_directions.end(), South) != adjacent_directions.end() || std::find(adjacent_directions.begin(), adjacent_directions.end(), East) != adjacent_directions.end()) && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) == adjacent_directions.end()) {
2027 		transition_type = SoutheastOuterTransitionType;
2028 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2029 		transition_type = NorthwestNortheastSouthwestSoutheastInnerTransitionType;
2030 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2031 		transition_type = NorthwestNortheastSouthwestInnerTransitionType;
2032 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2033 		transition_type = NorthwestNortheastSoutheastInnerTransitionType;
2034 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2035 		transition_type = NorthwestSouthwestSoutheastInnerTransitionType;
2036 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2037 		transition_type = NortheastSouthwestSoutheastInnerTransitionType;
2038 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2039 		transition_type = NorthwestNortheastInnerTransitionType;
2040 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2041 		transition_type = SouthwestSoutheastInnerTransitionType;
2042 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2043 		transition_type = NorthwestSouthwestInnerTransitionType;
2044 	} else if (allow_single && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2045 		transition_type = NortheastSoutheastInnerTransitionType;
2046 	} else if (std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2047 		transition_type = NorthwestSoutheastInnerTransitionType;
2048 	} else if (std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2049 		transition_type = NortheastSouthwestInnerTransitionType;
2050 	} else if (std::find(adjacent_directions.begin(), adjacent_directions.end(), Northwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2051 		transition_type = NorthwestInnerTransitionType;
2052 	} else if (std::find(adjacent_directions.begin(), adjacent_directions.end(), Northeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2053 		transition_type = NortheastInnerTransitionType;
2054 	} else if (std::find(adjacent_directions.begin(), adjacent_directions.end(), Southwest) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2055 		transition_type = SouthwestInnerTransitionType;
2056 	} else if (std::find(adjacent_directions.begin(), adjacent_directions.end(), Southeast) != adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), North) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), South) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), West) == adjacent_directions.end() && std::find(adjacent_directions.begin(), adjacent_directions.end(), East) == adjacent_directions.end()) {
2057 		transition_type = SoutheastInnerTransitionType;
2058 	}
2059 
2060 	return transition_type;
2061 }
2062 
CalculateTileTransitions(const Vec2i & pos,bool overlay,int z)2063 void CMap::CalculateTileTransitions(const Vec2i &pos, bool overlay, int z)
2064 {
2065 	CMapField &mf = *this->Field(pos, z);
2066 	CTerrainType *terrain = nullptr;
2067 	if (overlay) {
2068 		terrain = mf.OverlayTerrain;
2069 		mf.OverlayTransitionTiles.clear();
2070 	} else {
2071 		terrain = mf.Terrain;
2072 		mf.TransitionTiles.clear();
2073 	}
2074 
2075 	if (!terrain || (overlay && mf.OverlayTerrainDestroyed)) {
2076 		return;
2077 	}
2078 
2079 	int terrain_id = terrain->ID;
2080 
2081 	std::map<int, std::vector<int>> adjacent_terrain_directions;
2082 
2083 	for (int x_offset = -1; x_offset <= 1; ++x_offset) {
2084 		for (int y_offset = -1; y_offset <= 1; ++y_offset) {
2085 			if (x_offset != 0 || y_offset != 0) {
2086 				Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
2087 				if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
2088 					CTerrainType *adjacent_terrain = this->GetTileTerrain(adjacent_pos, overlay, z);
2089 					if (overlay && adjacent_terrain && this->Field(adjacent_pos, z)->OverlayTerrainDestroyed) {
2090 						adjacent_terrain = nullptr;
2091 					}
2092 					if (adjacent_terrain && terrain != adjacent_terrain) {
2093 						if (std::find(terrain->InnerBorderTerrains.begin(), terrain->InnerBorderTerrains.end(), adjacent_terrain) != terrain->InnerBorderTerrains.end()) {
2094 							adjacent_terrain_directions[adjacent_terrain->ID].push_back(GetDirectionFromOffset(x_offset, y_offset));
2095 						} else if (std::find(terrain->BorderTerrains.begin(), terrain->BorderTerrains.end(), adjacent_terrain) == terrain->BorderTerrains.end()) { //if the two terrain types can't border, look for a third terrain type which can border both, and which treats both as outer border terrains, and then use for transitions between both tiles
2096 							for (size_t i = 0; i < terrain->BorderTerrains.size(); ++i) {
2097 								CTerrainType *border_terrain = terrain->BorderTerrains[i];
2098 								if (std::find(terrain->InnerBorderTerrains.begin(), terrain->InnerBorderTerrains.end(), border_terrain) != terrain->InnerBorderTerrains.end() && std::find(adjacent_terrain->InnerBorderTerrains.begin(), adjacent_terrain->InnerBorderTerrains.end(), border_terrain) != adjacent_terrain->InnerBorderTerrains.end()) {
2099 									adjacent_terrain_directions[border_terrain->ID].push_back(GetDirectionFromOffset(x_offset, y_offset));
2100 									break;
2101 								}
2102 							}
2103 						}
2104 					}
2105 					if (!adjacent_terrain || (overlay && terrain != adjacent_terrain && std::find(terrain->BorderTerrains.begin(), terrain->BorderTerrains.end(), adjacent_terrain) == terrain->BorderTerrains.end())) { // happens if terrain is null or if it is an overlay tile which doesn't have a border with this one, so that i.e. tree transitions display correctly when adjacent to tiles without overlays
2106 						adjacent_terrain_directions[CTerrainType::TerrainTypes.size()].push_back(GetDirectionFromOffset(x_offset, y_offset));
2107 					}
2108 				}
2109 			}
2110 		}
2111 	}
2112 
2113 	for (std::map<int, std::vector<int>>::iterator iterator = adjacent_terrain_directions.begin(); iterator != adjacent_terrain_directions.end(); ++iterator) {
2114 		int adjacent_terrain_id = iterator->first;
2115 		CTerrainType *adjacent_terrain = adjacent_terrain_id < (int) CTerrainType::TerrainTypes.size() ? CTerrainType::TerrainTypes[adjacent_terrain_id] : nullptr;
2116 		int transition_type = GetTransitionType(iterator->second, terrain->AllowSingle);
2117 
2118 		if (transition_type != -1) {
2119 			bool found_transition = false;
2120 
2121 			if (!overlay) {
2122 				if (adjacent_terrain) {
2123 					if (terrain->TransitionTiles[std::tuple<int, int>(adjacent_terrain_id, transition_type)].size() > 0) {
2124 						mf.TransitionTiles.push_back(std::pair<CTerrainType *, int>(terrain, terrain->TransitionTiles[std::tuple<int, int>(adjacent_terrain_id, transition_type)][SyncRand(terrain->TransitionTiles[std::tuple<int, int>(adjacent_terrain_id, transition_type)].size())]));
2125 						found_transition = true;
2126 					} else if (adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(terrain_id, transition_type)].size() > 0) {
2127 						mf.TransitionTiles.push_back(std::pair<CTerrainType *, int>(adjacent_terrain, adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(terrain_id, transition_type)][SyncRand(adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(terrain_id, transition_type)].size())]));
2128 						found_transition = true;
2129 					} else if (adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(-1, transition_type)].size() > 0) {
2130 						mf.TransitionTiles.push_back(std::pair<CTerrainType *, int>(adjacent_terrain, adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(-1, transition_type)][SyncRand(adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(-1, transition_type)].size())]));
2131 						found_transition = true;
2132 					}
2133 				} else {
2134 					if (terrain->TransitionTiles[std::tuple<int, int>(-1, transition_type)].size() > 0) {
2135 						mf.TransitionTiles.push_back(std::pair<CTerrainType *, int>(terrain, terrain->TransitionTiles[std::tuple<int, int>(-1, transition_type)][SyncRand(terrain->TransitionTiles[std::tuple<int, int>(-1, transition_type)].size())]));
2136 					}
2137 				}
2138 			} else {
2139 				if (adjacent_terrain) {
2140 					if (adjacent_terrain && terrain->TransitionTiles[std::tuple<int, int>(adjacent_terrain_id, transition_type)].size() > 0) {
2141 						mf.OverlayTransitionTiles.push_back(std::pair<CTerrainType *, int>(terrain, terrain->TransitionTiles[std::tuple<int, int>(adjacent_terrain_id, transition_type)][SyncRand(terrain->TransitionTiles[std::tuple<int, int>(adjacent_terrain_id, transition_type)].size())]));
2142 						found_transition = true;
2143 					} else if (adjacent_terrain && adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(terrain_id, transition_type)].size() > 0) {
2144 						mf.OverlayTransitionTiles.push_back(std::pair<CTerrainType *, int>(adjacent_terrain, adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(terrain_id, transition_type)][SyncRand(adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(terrain_id, transition_type)].size())]));
2145 						found_transition = true;
2146 					} else if (adjacent_terrain && adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(-1, transition_type)].size() > 0) {
2147 						mf.OverlayTransitionTiles.push_back(std::pair<CTerrainType *, int>(adjacent_terrain, adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(-1, transition_type)][SyncRand(adjacent_terrain->AdjacentTransitionTiles[std::tuple<int, int>(-1, transition_type)].size())]));
2148 						found_transition = true;
2149 					}
2150 				} else {
2151 					if (terrain->TransitionTiles[std::tuple<int, int>(-1, transition_type)].size() > 0) {
2152 						mf.OverlayTransitionTiles.push_back(std::pair<CTerrainType *, int>(terrain, terrain->TransitionTiles[std::tuple<int, int>(-1, transition_type)][SyncRand(terrain->TransitionTiles[std::tuple<int, int>(-1, transition_type)].size())]));
2153 					}
2154 				}
2155 
2156 				if ((mf.Flags & MapFieldWaterAllowed) && (!adjacent_terrain || !(adjacent_terrain->Flags & MapFieldWaterAllowed))) { //if this is a water tile adjacent to a non-water tile, replace the water flag with a coast one
2157 					mf.Flags &= ~(MapFieldWaterAllowed);
2158 					mf.Flags |= MapFieldCoastAllowed;
2159 				}
2160 			}
2161 
2162 			if (adjacent_terrain && found_transition) {
2163 				for (size_t i = 0; i != iterator->second.size(); ++i) {
2164 					adjacent_terrain_directions[CTerrainType::TerrainTypes.size()].erase(std::remove(adjacent_terrain_directions[CTerrainType::TerrainTypes.size()].begin(), adjacent_terrain_directions[CTerrainType::TerrainTypes.size()].end(), iterator->second[i]), adjacent_terrain_directions[CTerrainType::TerrainTypes.size()].end());
2165 				}
2166 			}
2167 		}
2168 	}
2169 
2170 	//sort the transitions so that they will be displayed in the correct order
2171 	if (overlay) {
2172 		bool swapped = true;
2173 		for (int passes = 0; passes < (int) mf.OverlayTransitionTiles.size() && swapped; ++passes) {
2174 			swapped = false;
2175 			for (int i = 0; i < ((int) mf.OverlayTransitionTiles.size()) - 1; ++i) {
2176 				bool change_order = false;
2177 				if (std::find(mf.OverlayTransitionTiles[i + 1].first->InnerBorderTerrains.begin(), mf.OverlayTransitionTiles[i + 1].first->InnerBorderTerrains.end(), mf.OverlayTransitionTiles[i].first) != mf.OverlayTransitionTiles[i + 1].first->InnerBorderTerrains.end()) {
2178 					std::pair<CTerrainType *, int> temp_transition = mf.OverlayTransitionTiles[i];
2179 					mf.OverlayTransitionTiles[i] = mf.OverlayTransitionTiles[i + 1];
2180 					mf.OverlayTransitionTiles[i + 1] = temp_transition;
2181 					swapped = true;
2182 				}
2183 			}
2184 		}
2185 	} else {
2186 		bool swapped = true;
2187 		for (int passes = 0; passes < (int) mf.TransitionTiles.size() && swapped; ++passes) {
2188 			swapped = false;
2189 			for (int i = 0; i < ((int) mf.TransitionTiles.size()) - 1; ++i) {
2190 				bool change_order = false;
2191 				if (std::find(mf.TransitionTiles[i + 1].first->InnerBorderTerrains.begin(), mf.TransitionTiles[i + 1].first->InnerBorderTerrains.end(), mf.TransitionTiles[i].first) != mf.TransitionTiles[i + 1].first->InnerBorderTerrains.end()) {
2192 					std::pair<CTerrainType *, int> temp_transition = mf.TransitionTiles[i];
2193 					mf.TransitionTiles[i] = mf.TransitionTiles[i + 1];
2194 					mf.TransitionTiles[i + 1] = temp_transition;
2195 					swapped = true;
2196 				}
2197 			}
2198 		}
2199 	}
2200 }
2201 
CalculateTileLandmass(const Vec2i & pos,int z)2202 void CMap::CalculateTileLandmass(const Vec2i &pos, int z)
2203 {
2204 	if (!this->Info.IsPointOnMap(pos, z)) {
2205 		return;
2206 	}
2207 
2208 	if (Editor.Running != EditorNotRunning) { //no need to assign landmasses while in the editor
2209 		return;
2210 	}
2211 
2212 	CMapField &mf = *this->Field(pos, z);
2213 
2214 	if (mf.Landmass != 0) {
2215 		return; //already calculated
2216 	}
2217 
2218 	bool is_water = (mf.Flags & MapFieldWaterAllowed) || (mf.Flags & MapFieldCoastAllowed);
2219 
2220 	//doesn't have a landmass ID, and hasn't inherited one from another tile yet, so add a new one
2221 	mf.Landmass = this->Landmasses + 1;
2222 	this->Landmasses += 1;
2223 	this->BorderLandmasses.resize(this->Landmasses + 1);
2224 	//now, spread the new landmass ID to neighboring land tiles
2225 	std::vector<Vec2i> landmass_tiles;
2226 	landmass_tiles.push_back(pos);
2227 	//calculate the landmass of any neighboring land tiles with no set landmass as well
2228 	for (size_t i = 0; i < landmass_tiles.size(); ++i) {
2229 		for (int x_offset = -1; x_offset <= 1; ++x_offset) {
2230 			for (int y_offset = -1; y_offset <= 1; ++y_offset) {
2231 				if (x_offset != 0 || y_offset != 0) {
2232 					Vec2i adjacent_pos(landmass_tiles[i].x + x_offset, landmass_tiles[i].y + y_offset);
2233 					if (this->Info.IsPointOnMap(adjacent_pos, z)) {
2234 						CMapField &adjacent_mf = *this->Field(adjacent_pos, z);
2235 						bool adjacent_is_water = (adjacent_mf.Flags & MapFieldWaterAllowed) || (adjacent_mf.Flags & MapFieldCoastAllowed);
2236 
2237 						if (adjacent_is_water == is_water && adjacent_mf.Landmass == 0) {
2238 							adjacent_mf.Landmass = mf.Landmass;
2239 							landmass_tiles.push_back(adjacent_pos);
2240 						} else if (adjacent_is_water != is_water && adjacent_mf.Landmass != 0 && std::find(this->BorderLandmasses[mf.Landmass].begin(), this->BorderLandmasses[mf.Landmass].end(), adjacent_mf.Landmass) == this->BorderLandmasses[mf.Landmass].end()) {
2241 							this->BorderLandmasses[mf.Landmass].push_back(adjacent_mf.Landmass);
2242 							this->BorderLandmasses[adjacent_mf.Landmass].push_back(mf.Landmass);
2243 						}
2244 					}
2245 				}
2246 			}
2247 		}
2248 	}
2249 }
2250 
CalculateTileTerrainFeature(const Vec2i & pos,int z)2251 void CMap::CalculateTileTerrainFeature(const Vec2i &pos, int z)
2252 {
2253 	if (!this->Info.IsPointOnMap(pos, z)) {
2254 		return;
2255 	}
2256 
2257 	if (Editor.Running != EditorNotRunning) { //no need to assign terrain features while in the editor
2258 		return;
2259 	}
2260 
2261 	CMapField &mf = *this->Field(pos, z);
2262 
2263 	if (mf.TerrainFeature) {
2264 		return; //already has a terrain feature
2265 	}
2266 
2267 	//if any adjacent tile the same top terrain as this one, and has a terrain feature, then use that
2268 	for (int x_offset = -1; x_offset <= 1; ++x_offset) {
2269 		for (int y_offset = -1; y_offset <= 1; ++y_offset) {
2270 			if ((x_offset != 0 || y_offset != 0) && !(x_offset != 0 && y_offset != 0)) { //only directly adjacent tiles (no diagonal ones, and not the same tile)
2271 				Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
2272 				if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
2273 					CMapField &adjacent_mf = *this->Field(adjacent_pos, z);
2274 
2275 					if (adjacent_mf.TerrainFeature && adjacent_mf.TerrainFeature->TerrainType == GetTileTopTerrain(pos, false, z)) {
2276 						mf.TerrainFeature = adjacent_mf.TerrainFeature;
2277 						return;
2278 					}
2279 				}
2280 			}
2281 		}
2282 	}
2283 }
2284 
CalculateTileOwnership(const Vec2i & pos,int z)2285 void CMap::CalculateTileOwnership(const Vec2i &pos, int z)
2286 {
2287 	if (!this->Info.IsPointOnMap(pos, z)) {
2288 		return;
2289 	}
2290 
2291 	CMapField &mf = *this->Field(pos, z);
2292 
2293 	int new_owner = -1;
2294 	bool must_have_no_owner = false;
2295 
2296 	if (mf.Flags & MapFieldBuilding) { //make sure the place a building is located is set to be owned by its player; this is necessary for scenarios, since when they start buildings could be on another player's territory (i.e. if a farm starts next to a town hall)
2297 		const CUnitCache &cache = mf.UnitCache;
2298 		for (size_t i = 0; i != cache.size(); ++i) {
2299 			CUnit *unit = cache[i];
2300 			if (!unit) {
2301 				fprintf(stderr, "Error in CMap::CalculateTileOwnership (pos %d, %d): a unit in the tile's unit cache is null.\n", pos.x, pos.y);
2302 			}
2303 			if (unit->IsAliveOnMap() && unit->Type->BoolFlag[BUILDING_INDEX].value) {
2304 				if (unit->Variable[OWNERSHIPINFLUENCERANGE_INDEX].Value && unit->Player->Index != PlayerNumNeutral) {
2305 					new_owner = unit->Player->Index;
2306 					break;
2307 				} else if (unit->Player->Index == PlayerNumNeutral && (unit->Type == SettlementSiteUnitType || (unit->Type->GivesResource && !unit->Type->BoolFlag[CANHARVEST_INDEX].value))) { //there cannot be an owner for the tile of a (neutral) settlement site or deposit, otherwise players might not be able to build over them
2308 					must_have_no_owner = true;
2309 					break;
2310 				}
2311 			}
2312 		}
2313 	}
2314 
2315 	if (new_owner == -1 && !must_have_no_owner) { //if no building is on the tile, set it to the first unit to have influence on it, if that isn't blocked by an obstacle
2316 		std::vector<unsigned long> obstacle_flags;
2317 		obstacle_flags.push_back(MapFieldCoastAllowed);
2318 		obstacle_flags.push_back(MapFieldUnpassable);
2319 
2320 		std::vector<CUnit *> table;
2321 		Select(pos - Vec2i(16, 16), pos + Vec2i(16, 16), table, z);
2322 		for (size_t i = 0; i != table.size(); ++i) {
2323 			CUnit *unit = table[i];
2324 			if (!unit) {
2325 				fprintf(stderr, "Error in CMap::CalculateTileOwnership (pos %d, %d): a unit within the tile's range is null.\n", pos.x, pos.y);
2326 			}
2327 			if (unit->IsAliveOnMap() && unit->Variable[OWNERSHIPINFLUENCERANGE_INDEX].Value > 0 && unit->MapDistanceTo(pos, z) <= unit->Variable[OWNERSHIPINFLUENCERANGE_INDEX].Value) {
2328 				bool obstacle_check = true;
2329 				for (size_t j = 0; j < obstacle_flags.size(); ++j) {
2330 					bool obstacle_subcheck = false;
2331 					for (int x = 0; x < unit->Type->TileSize.x; ++x) {
2332 						for (int y = 0; y < unit->Type->TileSize.y; ++y) {
2333 							if (CheckObstaclesBetweenTiles(unit->tilePos + Vec2i(x, y), pos, obstacle_flags[j], z, 0, nullptr, unit->Player->Index)) { //the obstacle must be avoidable from at least one of the unit's tiles
2334 								obstacle_subcheck = true;
2335 								break;
2336 							}
2337 						}
2338 						if (obstacle_subcheck) {
2339 							break;
2340 						}
2341 					}
2342 					if (!obstacle_subcheck) {
2343 						obstacle_check = false;
2344 						break;
2345 					}
2346 				}
2347 				if (!obstacle_check) {
2348 					continue;
2349 				}
2350 				new_owner = unit->Player->Index;
2351 				break;
2352 			}
2353 		}
2354 	}
2355 
2356 	if (new_owner != mf.Owner) {
2357 		mf.Owner = new_owner;
2358 
2359 		this->CalculateTileOwnershipTransition(pos, z);
2360 
2361 		for (int x_offset = -1; x_offset <= 1; ++x_offset) {
2362 			for (int y_offset = -1; y_offset <= 1; ++y_offset) {
2363 				if (x_offset != 0 || y_offset != 0) {
2364 					Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
2365 					if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
2366 						CMapField &adjacent_mf = *this->Field(adjacent_pos, z);
2367 
2368 						this->CalculateTileOwnershipTransition(adjacent_pos, z);
2369 					}
2370 				}
2371 			}
2372 		}
2373 	}
2374 }
2375 
CalculateTileOwnershipTransition(const Vec2i & pos,int z)2376 void CMap::CalculateTileOwnershipTransition(const Vec2i &pos, int z)
2377 {
2378 	if (!this->Info.IsPointOnMap(pos, z)) {
2379 		return;
2380 	}
2381 
2382 	if (Editor.Running != EditorNotRunning) { //no need to assign ownership transitions while in the editor
2383 		return;
2384 	}
2385 
2386 	CMapField &mf = *this->Field(pos, z);
2387 
2388 	mf.OwnershipBorderTile = -1;
2389 
2390 	if (mf.Owner == -1) {
2391 		return;
2392 	}
2393 
2394 	std::vector<int> adjacent_directions;
2395 
2396 	for (int x_offset = -1; x_offset <= 1; ++x_offset) {
2397 		for (int y_offset = -1; y_offset <= 1; ++y_offset) {
2398 			if (x_offset != 0 || y_offset != 0) {
2399 				Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
2400 				if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
2401 					CMapField &adjacent_mf = *this->Field(adjacent_pos, z);
2402 					if (adjacent_mf.Owner != mf.Owner) {
2403 						adjacent_directions.push_back(GetDirectionFromOffset(x_offset, y_offset));
2404 					}
2405 				}
2406 			}
2407 		}
2408 	}
2409 
2410 	int transition_type = GetTransitionType(adjacent_directions, true);
2411 
2412 	if (transition_type != -1) {
2413 		if (Map.BorderTerrain->TransitionTiles[std::tuple<int, int>(-1, transition_type)].size() > 0) {
2414 			mf.OwnershipBorderTile = Map.BorderTerrain->TransitionTiles[std::tuple<int, int>(-1, transition_type)][SyncRand(Map.BorderTerrain->TransitionTiles[std::tuple<int, int>(-1, transition_type)].size())];
2415 		}
2416 	}
2417 }
2418 
AdjustMap()2419 void CMap::AdjustMap()
2420 {
2421 	for (size_t z = 0; z < this->MapLayers.size(); ++z) {
2422 		Vec2i map_start_pos(0, 0);
2423 		Vec2i map_end(this->Info.MapWidths[z], this->Info.MapHeights[z]);
2424 
2425 		this->AdjustTileMapIrregularities(false, map_start_pos, map_end, z);
2426 		this->AdjustTileMapIrregularities(true, map_start_pos, map_end, z);
2427 		this->AdjustTileMapTransitions(map_start_pos, map_end, z);
2428 		this->AdjustTileMapIrregularities(false, map_start_pos, map_end, z);
2429 		this->AdjustTileMapIrregularities(true, map_start_pos, map_end, z);
2430 	}
2431 }
2432 
AdjustTileMapIrregularities(bool overlay,const Vec2i & min_pos,const Vec2i & max_pos,int z)2433 void CMap::AdjustTileMapIrregularities(bool overlay, const Vec2i &min_pos, const Vec2i &max_pos, int z)
2434 {
2435 	bool no_irregularities_found = false;
2436 	while (!no_irregularities_found) {
2437 		no_irregularities_found = true;
2438 		for (int x = min_pos.x; x < max_pos.x; ++x) {
2439 			for (int y = min_pos.y; y < max_pos.y; ++y) {
2440 				CMapField &mf = *this->Field(x, y, z);
2441 				CTerrainType *terrain = overlay ? mf.OverlayTerrain : mf.Terrain;
2442 				if (!terrain || terrain->AllowSingle) {
2443 					continue;
2444 				}
2445 				std::vector<CTerrainType *> acceptable_adjacent_tile_types;
2446 				acceptable_adjacent_tile_types.push_back(terrain);
2447 				for (size_t i = 0; i < terrain->OuterBorderTerrains.size(); ++i) {
2448 					acceptable_adjacent_tile_types.push_back(terrain->OuterBorderTerrains[i]);
2449 				}
2450 
2451 				int horizontal_adjacent_tiles = 0;
2452 				int vertical_adjacent_tiles = 0;
2453 				int nw_quadrant_adjacent_tiles = 0; //should be 4 if the wrong tile types are present in X-1,Y; X-1,Y-1; X,Y-1; and X+1,Y+1
2454 				int ne_quadrant_adjacent_tiles = 0;
2455 				int sw_quadrant_adjacent_tiles = 0;
2456 				int se_quadrant_adjacent_tiles = 0;
2457 
2458 				if ((x - 1) >= 0 && std::find(acceptable_adjacent_tile_types.begin(), acceptable_adjacent_tile_types.end(), this->GetTileTerrain(Vec2i(x - 1, y), overlay, z)) == acceptable_adjacent_tile_types.end()) {
2459 					horizontal_adjacent_tiles += 1;
2460 					nw_quadrant_adjacent_tiles += 1;
2461 					sw_quadrant_adjacent_tiles += 1;
2462 				}
2463 				if ((x + 1) < this->Info.MapWidths[z] && std::find(acceptable_adjacent_tile_types.begin(), acceptable_adjacent_tile_types.end(), this->GetTileTerrain(Vec2i(x + 1, y), overlay, z)) == acceptable_adjacent_tile_types.end()) {
2464 					horizontal_adjacent_tiles += 1;
2465 					ne_quadrant_adjacent_tiles += 1;
2466 					se_quadrant_adjacent_tiles += 1;
2467 				}
2468 
2469 				if ((y - 1) >= 0 && std::find(acceptable_adjacent_tile_types.begin(), acceptable_adjacent_tile_types.end(), this->GetTileTerrain(Vec2i(x, y - 1), overlay, z)) == acceptable_adjacent_tile_types.end()) {
2470 					vertical_adjacent_tiles += 1;
2471 					nw_quadrant_adjacent_tiles += 1;
2472 					ne_quadrant_adjacent_tiles += 1;
2473 				}
2474 				if ((y + 1) < this->Info.MapHeights[z] && std::find(acceptable_adjacent_tile_types.begin(), acceptable_adjacent_tile_types.end(), this->GetTileTerrain(Vec2i(x, y + 1), overlay, z)) == acceptable_adjacent_tile_types.end()) {
2475 					vertical_adjacent_tiles += 1;
2476 					sw_quadrant_adjacent_tiles += 1;
2477 					se_quadrant_adjacent_tiles += 1;
2478 				}
2479 
2480 				if ((x - 1) >= 0 && (y - 1) >= 0 && std::find(acceptable_adjacent_tile_types.begin(), acceptable_adjacent_tile_types.end(), this->GetTileTerrain(Vec2i(x - 1, y - 1), overlay, z)) == acceptable_adjacent_tile_types.end()) {
2481 					nw_quadrant_adjacent_tiles += 1;
2482 					se_quadrant_adjacent_tiles += 1;
2483 				}
2484 				if ((x - 1) >= 0 && (y + 1) < this->Info.MapHeights[z] && std::find(acceptable_adjacent_tile_types.begin(), acceptable_adjacent_tile_types.end(), GetTileTerrain(Vec2i(x - 1, y + 1), overlay, z)) == acceptable_adjacent_tile_types.end()) {
2485 					sw_quadrant_adjacent_tiles += 1;
2486 					ne_quadrant_adjacent_tiles += 1;
2487 				}
2488 				if ((x + 1) < this->Info.MapWidths[z] && (y - 1) >= 0 && std::find(acceptable_adjacent_tile_types.begin(), acceptable_adjacent_tile_types.end(), GetTileTerrain(Vec2i(x + 1, y - 1), overlay, z)) == acceptable_adjacent_tile_types.end()) {
2489 					ne_quadrant_adjacent_tiles += 1;
2490 					sw_quadrant_adjacent_tiles += 1;
2491 				}
2492 				if ((x + 1) < this->Info.MapWidths[z] && (y + 1) < this->Info.MapHeights[z] && std::find(acceptable_adjacent_tile_types.begin(), acceptable_adjacent_tile_types.end(), GetTileTerrain(Vec2i(x + 1, y + 1), overlay, z)) == acceptable_adjacent_tile_types.end()) {
2493 					se_quadrant_adjacent_tiles += 1;
2494 					nw_quadrant_adjacent_tiles += 1;
2495 				}
2496 
2497 
2498 				if (horizontal_adjacent_tiles >= 2 || vertical_adjacent_tiles >= 2 || nw_quadrant_adjacent_tiles >= 4 || ne_quadrant_adjacent_tiles >= 4 || sw_quadrant_adjacent_tiles >= 4 || se_quadrant_adjacent_tiles >= 4) {
2499 					if (overlay) {
2500 						mf.RemoveOverlayTerrain();
2501 					} else {
2502 						bool changed_terrain = false;
2503 						for (int sub_x = -1; sub_x <= 1; ++sub_x) {
2504 							for (int sub_y = -1; sub_y <= 1; ++sub_y) {
2505 								if ((x + sub_x) < min_pos.x || (x + sub_x) >= max_pos.x || (y + sub_y) < min_pos.y || (y + sub_y) >= max_pos.y || (sub_x == 0 && sub_y == 0)) {
2506 									continue;
2507 								}
2508 								CTerrainType *tile_terrain = GetTileTerrain(Vec2i(x + sub_x, y + sub_y), false, z);
2509 								if (mf.Terrain != tile_terrain) {
2510 									if (std::find(mf.Terrain->InnerBorderTerrains.begin(), mf.Terrain->InnerBorderTerrains.end(), tile_terrain) != mf.Terrain->InnerBorderTerrains.end()) {
2511 										mf.SetTerrain(tile_terrain);
2512 										changed_terrain = true;
2513 										break;
2514 									}
2515 								}
2516 							}
2517 							if (changed_terrain) {
2518 								break;
2519 							}
2520 						}
2521 						if (!changed_terrain && terrain->InnerBorderTerrains.size() > 0) {
2522 							mf.SetTerrain(terrain->InnerBorderTerrains[0]);
2523 						}
2524 					}
2525 					no_irregularities_found = false;
2526 				}
2527 			}
2528 		}
2529 	}
2530 }
2531 
AdjustTileMapTransitions(const Vec2i & min_pos,const Vec2i & max_pos,int z)2532 void CMap::AdjustTileMapTransitions(const Vec2i &min_pos, const Vec2i &max_pos, int z)
2533 {
2534 	for (int x = min_pos.x; x < max_pos.x; ++x) {
2535 		for (int y = min_pos.y; y < max_pos.y; ++y) {
2536 			CMapField &mf = *this->Field(x, y, z);
2537 
2538 			for (int sub_x = -1; sub_x <= 1; ++sub_x) {
2539 				for (int sub_y = -1; sub_y <= 1; ++sub_y) {
2540 					if ((x + sub_x) < min_pos.x || (x + sub_x) >= max_pos.x || (y + sub_y) < min_pos.y || (y + sub_y) >= max_pos.y || (sub_x == 0 && sub_y == 0)) {
2541 						continue;
2542 					}
2543 					CTerrainType *tile_terrain = GetTileTerrain(Vec2i(x + sub_x, y + sub_y), false, z);
2544 					CTerrainType *tile_top_terrain = GetTileTopTerrain(Vec2i(x + sub_x, y + sub_y), false, z);
2545 					if (
2546 						mf.Terrain != tile_terrain
2547 						&& tile_top_terrain->Overlay
2548 						&& tile_top_terrain != mf.OverlayTerrain
2549 						&& std::find(tile_terrain->OuterBorderTerrains.begin(), tile_terrain->OuterBorderTerrains.end(), mf.Terrain) == tile_terrain->OuterBorderTerrains.end()
2550 						&& std::find(tile_top_terrain->BaseTerrainTypes.begin(), tile_top_terrain->BaseTerrainTypes.end(), mf.Terrain) == tile_top_terrain->BaseTerrainTypes.end()
2551 					) {
2552 						mf.SetTerrain(tile_terrain);
2553 					}
2554 				}
2555 			}
2556 		}
2557 	}
2558 
2559 	for (int x = min_pos.x; x < max_pos.x; ++x) {
2560 		for (int y = min_pos.y; y < max_pos.y; ++y) {
2561 			CMapField &mf = *this->Field(x, y, z);
2562 
2563 			for (int sub_x = -1; sub_x <= 1; ++sub_x) {
2564 				for (int sub_y = -1; sub_y <= 1; ++sub_y) {
2565 					if ((x + sub_x) < min_pos.x || (x + sub_x) >= max_pos.x || (y + sub_y) < min_pos.y || (y + sub_y) >= max_pos.y || (sub_x == 0 && sub_y == 0)) {
2566 						continue;
2567 					}
2568 					CTerrainType *tile_terrain = GetTileTerrain(Vec2i(x + sub_x, y + sub_y), false, z);
2569 					if (mf.Terrain != tile_terrain && std::find(mf.Terrain->BorderTerrains.begin(), mf.Terrain->BorderTerrains.end(), tile_terrain) == mf.Terrain->BorderTerrains.end()) {
2570 						for (size_t i = 0; i < mf.Terrain->BorderTerrains.size(); ++i) {
2571 							CTerrainType *border_terrain = mf.Terrain->BorderTerrains[i];
2572 							if (std::find(border_terrain->BorderTerrains.begin(), border_terrain->BorderTerrains.end(), mf.Terrain) != border_terrain->BorderTerrains.end() && std::find(border_terrain->BorderTerrains.begin(), border_terrain->BorderTerrains.end(), tile_terrain) != border_terrain->BorderTerrains.end()) {
2573 								mf.SetTerrain(border_terrain);
2574 								break;
2575 							}
2576 						}
2577 					}
2578 				}
2579 			}
2580 		}
2581 	}
2582 }
2583 
2584 /**
2585 **	@brief	Generate a given terrain on the map
2586 **
2587 **	@param	generated_terrain	The terrain generation characteristics
2588 **	@param	min_pos				The minimum position in the map to generate the terrain on
2589 **	@param	max_pos				The maximum position in the map to generate the terrain on
2590 **	@param	preserve_coastline	Whether to avoid changing the coastline during terrain generation
2591 **	@param	z					The map layer to generate the terrain on
2592 */
GenerateTerrain(const CGeneratedTerrain * generated_terrain,const Vec2i & min_pos,const Vec2i & max_pos,const bool preserve_coastline,const int z)2593 void CMap::GenerateTerrain(const CGeneratedTerrain *generated_terrain, const Vec2i &min_pos, const Vec2i &max_pos, const bool preserve_coastline, const int z)
2594 {
2595 	if (SaveGameLoading) {
2596 		return;
2597 	}
2598 
2599 	CTerrainType *terrain_type = generated_terrain->TerrainType;
2600 	const int seed_count = generated_terrain->SeedCount;
2601 	const int max_tile_quantity = (max_pos.x + 1 - min_pos.x) * (max_pos.y + 1 - min_pos.y) * generated_terrain->MaxPercent / 100;
2602 	int tile_quantity = 0;
2603 
2604 	Vec2i random_pos(0, 0);
2605 	int count = seed_count;
2606 
2607 	std::vector<Vec2i> seeds;
2608 
2609 	if (generated_terrain->UseExistingAsSeeds) { //use existing tiles of the given terrain as seeds for the terrain generation
2610 		for (int x = min_pos.x; x <= max_pos.x; ++x) {
2611 			for (int y = min_pos.y; y <= max_pos.y; ++y) {
2612 				const Vec2i tile_pos(x, y);
2613 				const CMapField *tile = this->Field(x, y, z);
2614 
2615 				if (max_tile_quantity != 0 && tile->GetTopTerrain() == terrain_type) {
2616 					tile_quantity++;
2617 				}
2618 
2619 				if (!generated_terrain->CanUseTileAsSeed(tile)) {
2620 					continue;
2621 				}
2622 
2623 				if (this->IsPointInASubtemplateArea(tile_pos, z)) {
2624 					continue;
2625 				}
2626 
2627 				seeds.push_back(tile_pos);
2628 			}
2629 		}
2630 	}
2631 
2632 	if (generated_terrain->UseSubtemplateBordersAsSeeds) {
2633 		for (size_t i = 0; i < this->MapLayers[z]->SubtemplateAreas.size(); ++i) {
2634 			const Vec2i subtemplate_min_pos = std::get<0>(this->MapLayers[z]->SubtemplateAreas[i]);
2635 			const Vec2i subtemplate_max_pos = std::get<1>(this->MapLayers[z]->SubtemplateAreas[i]);
2636 
2637 			for (int x = subtemplate_min_pos.x; x <= subtemplate_max_pos.x; ++x) {
2638 				for (int y = subtemplate_min_pos.y; y <= subtemplate_max_pos.y; ++y) {
2639 					const Vec2i tile_pos(x, y);
2640 					const CMapField *tile = this->Field(x, y, z);
2641 
2642 					if (!generated_terrain->CanUseTileAsSeed(tile)) {
2643 						continue;
2644 					}
2645 
2646 					if (!IsPointAdjacentToNonSubtemplateArea(tile_pos, z)) {
2647 						continue;
2648 					}
2649 
2650 					seeds.push_back(tile_pos);
2651 				}
2652 			}
2653 		}
2654 	}
2655 
2656 	std::vector<Vec2i> potential_positions;
2657 	for (int x = min_pos.x; x <= max_pos.x; ++x) {
2658 		for (int y = min_pos.y; y <= max_pos.y; ++y) {
2659 			potential_positions.push_back(Vec2i(x, y));
2660 		}
2661 	}
2662 
2663 	// create initial seeds
2664 	while (count > 0 && !potential_positions.empty()) {
2665 		if (max_tile_quantity != 0 && tile_quantity >= max_tile_quantity) {
2666 			break;
2667 		}
2668 
2669 		random_pos = potential_positions[SyncRand(potential_positions.size())];
2670 		potential_positions.erase(std::remove(potential_positions.begin(), potential_positions.end(), random_pos), potential_positions.end());
2671 
2672 		if (!this->Info.IsPointOnMap(random_pos, z) || this->IsPointInASubtemplateArea(random_pos, z)) {
2673 			continue;
2674 		}
2675 
2676 		CTerrainType *tile_terrain = this->GetTileTerrain(random_pos, false, z);
2677 
2678 		if (!generated_terrain->CanGenerateOnTile(this->Field(random_pos, z))) {
2679 			continue;
2680 		}
2681 
2682 		if (
2683 			(
2684 				(
2685 					!terrain_type->Overlay
2686 					&& ((tile_terrain == terrain_type && GetTileTopTerrain(random_pos, false, z)->Overlay) || (std::find(terrain_type->BorderTerrains.begin(), terrain_type->BorderTerrains.end(), tile_terrain) != terrain_type->BorderTerrains.end() && this->TileBordersOnlySameTerrain(random_pos, terrain_type, z)))
2687 				)
2688 				|| (
2689 					terrain_type->Overlay
2690 					&& std::find(terrain_type->BaseTerrainTypes.begin(), terrain_type->BaseTerrainTypes.end(), tile_terrain) != terrain_type->BaseTerrainTypes.end() && this->TileBordersOnlySameTerrain(random_pos, terrain_type, z)
2691 					&& (!GetTileTopTerrain(random_pos, false, z)->Overlay || GetTileTopTerrain(random_pos, false, z) == terrain_type)
2692 				)
2693 			)
2694 			&& (!preserve_coastline || (terrain_type->Flags & MapFieldWaterAllowed) == (tile_terrain->Flags & MapFieldWaterAllowed))
2695 			&& !this->TileHasUnitsIncompatibleWithTerrain(random_pos, terrain_type, z)
2696 			&& (!(terrain_type->Flags & MapFieldUnpassable) || !this->TileBordersUnit(random_pos, z)) // if the terrain is unpassable, don't expand to spots adjacent to units
2697 		) {
2698 			std::vector<Vec2i> adjacent_positions;
2699 			for (int sub_x = -1; sub_x <= 1; sub_x += 2) { // +2 so that only diagonals are used
2700 				for (int sub_y = -1; sub_y <= 1; sub_y += 2) {
2701 					Vec2i diagonal_pos(random_pos.x + sub_x, random_pos.y + sub_y);
2702 					Vec2i vertical_pos(random_pos.x, random_pos.y + sub_y);
2703 					Vec2i horizontal_pos(random_pos.x + sub_x, random_pos.y);
2704 					if (!this->Info.IsPointOnMap(diagonal_pos, z)) {
2705 						continue;
2706 					}
2707 
2708 					CTerrainType *diagonal_tile_terrain = this->GetTileTerrain(diagonal_pos, false, z);
2709 					CTerrainType *vertical_tile_terrain = this->GetTileTerrain(vertical_pos, false, z);
2710 					CTerrainType *horizontal_tile_terrain = this->GetTileTerrain(horizontal_pos, false, z);
2711 
2712 					if (
2713 						!generated_terrain->CanGenerateOnTile(this->Field(diagonal_pos, z))
2714 						|| !generated_terrain->CanGenerateOnTile(this->Field(vertical_pos, z))
2715 						|| !generated_terrain->CanGenerateOnTile(this->Field(horizontal_pos, z))
2716 					) {
2717 						continue;
2718 					}
2719 
2720 					if (
2721 						(
2722 							(
2723 								!terrain_type->Overlay
2724 								&& ((diagonal_tile_terrain == terrain_type && GetTileTopTerrain(diagonal_pos, false, z)->Overlay) || (std::find(terrain_type->BorderTerrains.begin(), terrain_type->BorderTerrains.end(), diagonal_tile_terrain) != terrain_type->BorderTerrains.end() && this->TileBordersOnlySameTerrain(diagonal_pos, terrain_type, z)))
2725 								&& ((vertical_tile_terrain == terrain_type && GetTileTopTerrain(vertical_pos, false, z)->Overlay) || (std::find(terrain_type->BorderTerrains.begin(), terrain_type->BorderTerrains.end(), vertical_tile_terrain) != terrain_type->BorderTerrains.end() && this->TileBordersOnlySameTerrain(vertical_pos, terrain_type, z)))
2726 								&& ((horizontal_tile_terrain == terrain_type && GetTileTopTerrain(horizontal_pos, false, z)->Overlay) || (std::find(terrain_type->BorderTerrains.begin(), terrain_type->BorderTerrains.end(), horizontal_tile_terrain) != terrain_type->BorderTerrains.end() && this->TileBordersOnlySameTerrain(horizontal_pos, terrain_type, z)))
2727 							)
2728 							|| (
2729 								terrain_type->Overlay
2730 								&& std::find(terrain_type->BaseTerrainTypes.begin(), terrain_type->BaseTerrainTypes.end(), diagonal_tile_terrain) != terrain_type->BaseTerrainTypes.end() && this->TileBordersOnlySameTerrain(diagonal_pos, terrain_type, z)
2731 								&& std::find(terrain_type->BaseTerrainTypes.begin(), terrain_type->BaseTerrainTypes.end(), vertical_tile_terrain) != terrain_type->BaseTerrainTypes.end() && this->TileBordersOnlySameTerrain(vertical_pos, terrain_type, z)
2732 								&& std::find(terrain_type->BaseTerrainTypes.begin(), terrain_type->BaseTerrainTypes.end(), horizontal_tile_terrain) != terrain_type->BaseTerrainTypes.end() && this->TileBordersOnlySameTerrain(horizontal_pos, terrain_type, z)
2733 								&& (!GetTileTopTerrain(diagonal_pos, false, z)->Overlay || GetTileTopTerrain(diagonal_pos, false, z) == terrain_type) && (!GetTileTopTerrain(vertical_pos, false, z)->Overlay || GetTileTopTerrain(vertical_pos, false, z) == terrain_type) && (!GetTileTopTerrain(horizontal_pos, false, z)->Overlay || GetTileTopTerrain(horizontal_pos, false, z) == terrain_type)
2734 							)
2735 						)
2736 						&& (!preserve_coastline || ((terrain_type->Flags & MapFieldWaterAllowed) == (diagonal_tile_terrain->Flags & MapFieldWaterAllowed) && (terrain_type->Flags & MapFieldWaterAllowed) == (vertical_tile_terrain->Flags & MapFieldWaterAllowed) && (terrain_type->Flags & MapFieldWaterAllowed) == (horizontal_tile_terrain->Flags & MapFieldWaterAllowed)))
2737 						&& !this->TileHasUnitsIncompatibleWithTerrain(diagonal_pos, terrain_type, z) && !this->TileHasUnitsIncompatibleWithTerrain(vertical_pos, terrain_type, z) && !this->TileHasUnitsIncompatibleWithTerrain(horizontal_pos, terrain_type, z)
2738 						&& (!(terrain_type->Flags & MapFieldUnpassable) || (!this->TileBordersUnit(diagonal_pos, z) && !this->TileBordersUnit(vertical_pos, z) && !this->TileBordersUnit(horizontal_pos, z))) // if the terrain is unpassable, don't expand to spots adjacent to buildings
2739 						&& !this->IsPointInASubtemplateArea(diagonal_pos, z) && !this->IsPointInASubtemplateArea(vertical_pos, z) && !this->IsPointInASubtemplateArea(horizontal_pos, z)
2740 					) {
2741 						adjacent_positions.push_back(diagonal_pos);
2742 					}
2743 				}
2744 			}
2745 
2746 			if (adjacent_positions.size() > 0) {
2747 				Vec2i adjacent_pos = adjacent_positions[SyncRand(adjacent_positions.size())];
2748 				if (!terrain_type->Overlay) {
2749 					this->Field(random_pos, z)->RemoveOverlayTerrain();
2750 					this->Field(adjacent_pos, z)->RemoveOverlayTerrain();
2751 					this->Field(Vec2i(random_pos.x, adjacent_pos.y), z)->RemoveOverlayTerrain();
2752 					this->Field(Vec2i(adjacent_pos.x, random_pos.y), z)->RemoveOverlayTerrain();
2753 				}
2754 				this->Field(random_pos, z)->SetTerrain(terrain_type);
2755 				this->Field(adjacent_pos, z)->SetTerrain(terrain_type);
2756 				this->Field(Vec2i(random_pos.x, adjacent_pos.y), z)->SetTerrain(terrain_type);
2757 				this->Field(Vec2i(adjacent_pos.x, random_pos.y), z)->SetTerrain(terrain_type);
2758 				count -= 1;
2759 				seeds.push_back(random_pos);
2760 				seeds.push_back(adjacent_pos);
2761 				seeds.push_back(Vec2i(random_pos.x, adjacent_pos.y));
2762 				seeds.push_back(Vec2i(adjacent_pos.x, random_pos.y));
2763 
2764 				tile_quantity += 4;
2765 			}
2766 		}
2767 	}
2768 
2769 	// expand seeds
2770 	for (size_t i = 0; i < seeds.size(); ++i) {
2771 		Vec2i seed_pos = seeds[i];
2772 
2773 		if (max_tile_quantity != 0 && tile_quantity >= max_tile_quantity) {
2774 			break;
2775 		}
2776 
2777 		const int random_number = SyncRand(100);
2778 		if (random_number >= generated_terrain->ExpansionChance) {
2779 			continue;
2780 		}
2781 
2782 		std::vector<Vec2i> adjacent_positions;
2783 		for (int sub_x = -1; sub_x <= 1; sub_x += 2) { // +2 so that only diagonals are used
2784 			for (int sub_y = -1; sub_y <= 1; sub_y += 2) {
2785 				Vec2i diagonal_pos(seed_pos.x + sub_x, seed_pos.y + sub_y);
2786 				Vec2i vertical_pos(seed_pos.x, seed_pos.y + sub_y);
2787 				Vec2i horizontal_pos(seed_pos.x + sub_x, seed_pos.y);
2788 				if (!this->Info.IsPointOnMap(diagonal_pos, z)) {
2789 					continue;
2790 				}
2791 
2792 				if ( //must either be able to generate on the tiles, or they must already have the generated terrain type
2793 					!generated_terrain->CanTileBePartOfExpansion(this->Field(diagonal_pos, z))
2794 					|| !generated_terrain->CanTileBePartOfExpansion(this->Field(vertical_pos, z))
2795 					|| !generated_terrain->CanTileBePartOfExpansion(this->Field(horizontal_pos, z))
2796 				) {
2797 					continue;
2798 				}
2799 
2800 				CTerrainType *diagonal_tile_terrain = this->GetTileTerrain(diagonal_pos, false, z);
2801 				CTerrainType *vertical_tile_terrain = this->GetTileTerrain(vertical_pos, false, z);
2802 				CTerrainType *horizontal_tile_terrain = this->GetTileTerrain(horizontal_pos, false, z);
2803 				CTerrainType *diagonal_tile_top_terrain = this->GetTileTopTerrain(diagonal_pos, false, z);
2804 				CTerrainType *vertical_tile_top_terrain = this->GetTileTopTerrain(vertical_pos, false, z);
2805 				CTerrainType *horizontal_tile_top_terrain = this->GetTileTopTerrain(horizontal_pos, false, z);
2806 
2807 				if (!terrain_type->Overlay) {
2808 					if (diagonal_tile_terrain != terrain_type && (std::find(terrain_type->BorderTerrains.begin(), terrain_type->BorderTerrains.end(), diagonal_tile_terrain) == terrain_type->BorderTerrains.end() || this->TileBordersTerrainIncompatibleWithTerrain(diagonal_pos, terrain_type, z))) {
2809 						continue;
2810 					}
2811 					if (vertical_tile_terrain != terrain_type && (std::find(terrain_type->BorderTerrains.begin(), terrain_type->BorderTerrains.end(), vertical_tile_terrain) == terrain_type->BorderTerrains.end() || this->TileBordersTerrainIncompatibleWithTerrain(vertical_pos, terrain_type, z))) {
2812 						continue;
2813 					}
2814 					if (horizontal_tile_terrain != terrain_type && (std::find(terrain_type->BorderTerrains.begin(), terrain_type->BorderTerrains.end(), horizontal_tile_terrain) == terrain_type->BorderTerrains.end() || this->TileBordersTerrainIncompatibleWithTerrain(horizontal_pos, terrain_type, z))) {
2815 						continue;
2816 					}
2817 				} else {
2818 					if ((std::find(terrain_type->BaseTerrainTypes.begin(), terrain_type->BaseTerrainTypes.end(), diagonal_tile_terrain) == terrain_type->BaseTerrainTypes.end() || this->TileBordersTerrainIncompatibleWithTerrain(diagonal_pos, terrain_type, z)) && GetTileTerrain(diagonal_pos, terrain_type->Overlay, z) != terrain_type) {
2819 						continue;
2820 					}
2821 					if ((std::find(terrain_type->BaseTerrainTypes.begin(), terrain_type->BaseTerrainTypes.end(), vertical_tile_terrain) == terrain_type->BaseTerrainTypes.end() || this->TileBordersTerrainIncompatibleWithTerrain(vertical_pos, terrain_type, z)) && GetTileTerrain(vertical_pos, terrain_type->Overlay, z) != terrain_type) {
2822 						continue;
2823 					}
2824 					if ((std::find(terrain_type->BaseTerrainTypes.begin(), terrain_type->BaseTerrainTypes.end(), horizontal_tile_terrain) == terrain_type->BaseTerrainTypes.end() || this->TileBordersTerrainIncompatibleWithTerrain(horizontal_pos, terrain_type, z)) && GetTileTerrain(horizontal_pos, terrain_type->Overlay, z) != terrain_type) {
2825 						continue;
2826 					}
2827 				}
2828 
2829 				if (diagonal_tile_top_terrain == terrain_type && vertical_tile_top_terrain == terrain_type && horizontal_tile_top_terrain == terrain_type) { //at least one of the tiles being expanded to must be different from the terrain type
2830 					continue;
2831 				}
2832 
2833 				//tiles within a subtemplate area can only be used as seeds, they cannot be modified themselves
2834 				if (
2835 					(this->IsPointInASubtemplateArea(diagonal_pos, z) && !generated_terrain->CanUseTileAsSeed(this->Field(diagonal_pos, z)))
2836 					|| (this->IsPointInASubtemplateArea(vertical_pos, z) && !generated_terrain->CanUseTileAsSeed(this->Field(vertical_pos, z)))
2837 					|| (this->IsPointInASubtemplateArea(horizontal_pos, z) && !generated_terrain->CanUseTileAsSeed(this->Field(horizontal_pos, z)))
2838 				) {
2839 					continue;
2840 				}
2841 
2842 				if (
2843 					preserve_coastline
2844 					&& (
2845 						(terrain_type->Flags & MapFieldWaterAllowed) != (diagonal_tile_terrain->Flags & MapFieldWaterAllowed)
2846 						|| (terrain_type->Flags & MapFieldWaterAllowed) != (vertical_tile_terrain->Flags & MapFieldWaterAllowed)
2847 						|| (terrain_type->Flags & MapFieldWaterAllowed) != (horizontal_tile_terrain->Flags & MapFieldWaterAllowed)
2848 					)
2849 				) {
2850 					continue;
2851 				}
2852 
2853 				if (this->TileHasUnitsIncompatibleWithTerrain(diagonal_pos, terrain_type, z) || this->TileHasUnitsIncompatibleWithTerrain(vertical_pos, terrain_type, z) || this->TileHasUnitsIncompatibleWithTerrain(horizontal_pos, terrain_type, z)) {
2854 					continue;
2855 				}
2856 
2857 				if ( // if the terrain is unpassable, don't expand to spots adjacent to buildings
2858 					(terrain_type->Flags & MapFieldUnpassable) && (this->TileBordersUnit(diagonal_pos, z) || this->TileBordersUnit(vertical_pos, z) || this->TileBordersUnit(horizontal_pos, z))
2859 				) {
2860 					continue;
2861 				}
2862 
2863 				adjacent_positions.push_back(diagonal_pos);
2864 			}
2865 		}
2866 
2867 		if (adjacent_positions.size() > 0) {
2868 			Vec2i adjacent_pos = adjacent_positions[SyncRand(adjacent_positions.size())];
2869 			Vec2i adjacent_pos_horizontal(adjacent_pos.x, seed_pos.y);
2870 			Vec2i adjacent_pos_vertical(seed_pos.x, adjacent_pos.y);
2871 
2872 			if (!this->IsPointInASubtemplateArea(adjacent_pos, z) && this->GetTileTopTerrain(adjacent_pos, false, z) != terrain_type && (this->GetTileTerrain(adjacent_pos, terrain_type->Overlay, z) != terrain_type || generated_terrain->CanRemoveTileOverlayTerrain(this->Field(adjacent_pos, z)))) {
2873 				if (!terrain_type->Overlay && generated_terrain->CanRemoveTileOverlayTerrain(this->Field(adjacent_pos, z))) {
2874 					this->Field(adjacent_pos, z)->RemoveOverlayTerrain();
2875 				}
2876 
2877 				if (this->GetTileTerrain(adjacent_pos, terrain_type->Overlay, z) != terrain_type) {
2878 					this->Field(adjacent_pos, z)->SetTerrain(terrain_type);
2879 				}
2880 
2881 				seeds.push_back(adjacent_pos);
2882 
2883 				if (this->GetTileTopTerrain(adjacent_pos, false, z) == terrain_type) {
2884 					tile_quantity++;
2885 				}
2886 			}
2887 
2888 			if (!this->IsPointInASubtemplateArea(adjacent_pos_horizontal, z) && this->GetTileTopTerrain(adjacent_pos_horizontal, false, z) != terrain_type && (this->GetTileTerrain(adjacent_pos_horizontal, terrain_type->Overlay, z) != terrain_type || generated_terrain->CanRemoveTileOverlayTerrain(this->Field(adjacent_pos_horizontal, z)))) {
2889 				if (!terrain_type->Overlay && generated_terrain->CanRemoveTileOverlayTerrain(this->Field(adjacent_pos_horizontal, z))) {
2890 					this->Field(adjacent_pos_horizontal, z)->RemoveOverlayTerrain();
2891 				}
2892 
2893 				if (this->GetTileTerrain(adjacent_pos_horizontal, terrain_type->Overlay, z) != terrain_type) {
2894 					this->Field(adjacent_pos_horizontal, z)->SetTerrain(terrain_type);
2895 				}
2896 
2897 				seeds.push_back(adjacent_pos_horizontal);
2898 
2899 				if (this->GetTileTopTerrain(adjacent_pos_horizontal, false, z) == terrain_type) {
2900 					tile_quantity++;
2901 				}
2902 			}
2903 
2904 			if (!this->IsPointInASubtemplateArea(adjacent_pos_vertical, z) && this->GetTileTopTerrain(adjacent_pos_vertical, false, z) != terrain_type && (this->GetTileTerrain(adjacent_pos_vertical, terrain_type->Overlay, z) != terrain_type || generated_terrain->CanRemoveTileOverlayTerrain(this->Field(adjacent_pos_vertical, z)))) {
2905 				if (!terrain_type->Overlay && generated_terrain->CanRemoveTileOverlayTerrain(this->Field(adjacent_pos_vertical, z))) {
2906 					this->Field(adjacent_pos_vertical, z)->RemoveOverlayTerrain();
2907 				}
2908 
2909 				if (this->GetTileTerrain(adjacent_pos_vertical, terrain_type->Overlay, z) != terrain_type) {
2910 					this->Field(adjacent_pos_vertical, z)->SetTerrain(terrain_type);
2911 				}
2912 
2913 				seeds.push_back(adjacent_pos_vertical);
2914 
2915 				if (this->GetTileTopTerrain(adjacent_pos_vertical, false, z) == terrain_type) {
2916 					tile_quantity++;
2917 				}
2918 			}
2919 		}
2920 	}
2921 }
2922 
GenerateNeutralUnits(CUnitType * unit_type,int quantity,const Vec2i & min_pos,const Vec2i & max_pos,bool grouped,int z)2923 void CMap::GenerateNeutralUnits(CUnitType *unit_type, int quantity, const Vec2i &min_pos, const Vec2i &max_pos, bool grouped, int z)
2924 {
2925 	if (SaveGameLoading) {
2926 		return;
2927 	}
2928 
2929 	Vec2i unit_pos(-1, -1);
2930 
2931 	for (int i = 0; i < quantity; ++i) {
2932 		if (i == 0 || !grouped) {
2933 			unit_pos = GenerateUnitLocation(unit_type, nullptr, min_pos, max_pos, z);
2934 		}
2935 		if (!this->Info.IsPointOnMap(unit_pos, z)) {
2936 			continue;
2937 		}
2938 		if (unit_type->GivesResource) {
2939 			CUnit *unit = CreateResourceUnit(unit_pos, *unit_type, z);
2940 		} else {
2941 			CUnit *unit = CreateUnit(unit_pos, *unit_type, &Players[PlayerNumNeutral], z, unit_type->BoolFlag[BUILDING_INDEX].value && unit_type->TileSize.x > 1 && unit_type->TileSize.y > 1);
2942 		}
2943 	}
2944 }
2945 //Wyrmgus end
2946 
2947 //Wyrmgus start
ClearOverlayTile(const Vec2i & pos,int z)2948 void CMap::ClearOverlayTile(const Vec2i &pos, int z)
2949 {
2950 	CMapField &mf = *this->Field(pos, z);
2951 
2952 	if (!mf.OverlayTerrain) {
2953 		return;
2954 	}
2955 
2956 	this->SetOverlayTerrainDestroyed(pos, true, z);
2957 
2958 	//remove decorations if a wall, tree or rock was removed from the tile
2959 	std::vector<CUnit *> table;
2960 	Select(pos, pos, table, z);
2961 	for (size_t i = 0; i != table.size(); ++i) {
2962 		if (table[i]->Type->UnitType == UnitTypeLand && table[i]->Type->BoolFlag[DECORATION_INDEX].value) {
2963 			if (Editor.Running == EditorNotRunning) {
2964 				LetUnitDie(*table[i]);
2965 			} else {
2966 				EditorActionRemoveUnit(*table[i], false);
2967 			}
2968 		}
2969 	}
2970 
2971 	//check if any further tile should be removed with the clearing of this one
2972 	if (!mf.OverlayTerrain->AllowSingle) {
2973 		for (int x_offset = -1; x_offset <= 1; ++x_offset) {
2974 			for (int y_offset = -1; y_offset <= 1; ++y_offset) {
2975 				if (x_offset != 0 || y_offset != 0) {
2976 					Vec2i adjacent_pos(pos.x + x_offset, pos.y + y_offset);
2977 					if (Map.Info.IsPointOnMap(adjacent_pos, z)) {
2978 						CMapField &adjacent_mf = *this->Field(adjacent_pos, z);
2979 
2980 						if (adjacent_mf.OverlayTerrain == mf.OverlayTerrain && !adjacent_mf.OverlayTerrainDestroyed && !this->CurrentTerrainCanBeAt(adjacent_pos, true, z)) {
2981 							this->ClearOverlayTile(adjacent_pos, z);
2982 						}
2983 					}
2984 				}
2985 			}
2986 		}
2987 	}
2988 }
2989 //Wyrmgus end
2990 
2991 //Wyrmgus start
2992 /*
2993 /// Remove wood from the map.
2994 void CMap::ClearWoodTile(const Vec2i &pos)
2995 {
2996 	CMapField &mf = *this->Field(pos);
2997 
2998 	mf.setGraphicTile(this->Tileset->getRemovedTreeTile());
2999 	mf.Flags &= ~(MapFieldForest | MapFieldUnpassable);
3000 	mf.Value = 0;
3001 
3002 	UI.Minimap.UpdateXY(pos);
3003 	FixNeighbors(MapFieldForest, 0, pos);
3004 
3005 	//maybe isExplored
3006 	if (mf.playerInfo.IsExplored(*ThisPlayer)) {
3007 		UI.Minimap.UpdateSeenXY(pos);
3008 		MarkSeenTile(mf);
3009 	}
3010 }
3011 
3012 /// Remove rock from the map.
3013 void CMap::ClearRockTile(const Vec2i &pos)
3014 {
3015 	CMapField &mf = *this->Field(pos);
3016 
3017 	mf.setGraphicTile(this->Tileset->getRemovedRockTile());
3018 	mf.Flags &= ~(MapFieldRocks | MapFieldUnpassable);
3019 	mf.Value = 0;
3020 
3021 	UI.Minimap.UpdateXY(pos);
3022 	FixNeighbors(MapFieldRocks, 0, pos);
3023 
3024 	//maybe isExplored
3025 	if (mf.playerInfo.IsExplored(*ThisPlayer)) {
3026 		UI.Minimap.UpdateSeenXY(pos);
3027 		MarkSeenTile(mf);
3028 	}
3029 }
3030 */
3031 //Wyrmgus end
3032 
3033 /**
3034 **	@brief	Regenerate forest.
3035 */
RegenerateForest()3036 void CMap::RegenerateForest()
3037 {
3038 	if (!ForestRegeneration) {
3039 		return;
3040 	}
3041 
3042 	for (CMapLayer *map_layer : this->MapLayers) {
3043 		map_layer->RegenerateForest();
3044 	}
3045 }
3046 
3047 
3048 /**
3049 **  Load the map presentation
3050 **
3051 **  @param mapname  map filename
3052 */
LoadStratagusMapInfo(const std::string & mapname)3053 void LoadStratagusMapInfo(const std::string &mapname)
3054 {
3055 	// Set the default map setup by replacing .smp with .sms
3056 	size_t loc = mapname.find(".smp");
3057 	if (loc != std::string::npos) {
3058 		Map.Info.Filename = mapname;
3059 		Map.Info.Filename.replace(loc, 4, ".sms");
3060 	}
3061 
3062 	const std::string filename = LibraryFileName(mapname.c_str());
3063 	LuaLoadFile(filename);
3064 }
3065 
3066 //@}
3067