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