1 /*
2  * Copyright (C) 2002-2020 by the Widelands Development Team
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  */
19 
20 #include "logic/map.h"
21 
22 #include <memory>
23 
24 #include "base/log.h"
25 #include "base/macros.h"
26 #include "base/scoped_timer.h"
27 #include "base/wexception.h"
28 #include "economy/flag.h"
29 #include "economy/roadbase.h"
30 #include "io/filesystem/filesystem_exceptions.h"
31 #include "io/filesystem/layered_filesystem.h"
32 #include "logic/filesystem_constants.h"
33 #include "logic/map_objects/checkstep.h"
34 #include "logic/map_objects/findimmovable.h"
35 #include "logic/map_objects/findnode.h"
36 #include "logic/map_objects/tribes/soldier.h"
37 #include "logic/map_objects/world/terrain_description.h"
38 #include "logic/map_objects/world/world.h"
39 #include "logic/mapfringeregion.h"
40 #include "logic/maphollowregion.h"
41 #include "logic/mapregion.h"
42 #include "logic/note_map_options.h"
43 #include "logic/objective.h"
44 #include "logic/pathfield.h"
45 #include "map_io/s2map.h"
46 #include "map_io/widelands_map_loader.h"
47 #include "notifications/notifications.h"
48 
49 namespace Widelands {
50 
FieldData(const Field & field)51 FieldData::FieldData(const Field& field)
52    : height(field.get_height()),
53      resources(field.get_resources()),
54      resource_amount(field.get_initial_res_amount()),
55      terrains(field.get_terrains()) {
56 	if (const BaseImmovable* imm = field.get_immovable()) {
57 		immovable = imm->descr().name();
58 	} else {
59 		immovable = "";
60 	}
61 	for (Bob* bob = field.get_first_bob(); bob; bob = bob->get_next_bob()) {
62 		bobs.push_back(bob->descr().name());
63 	}
64 }
65 
66 /*
67 ==============================================================================
68 
69 Map IMPLEMENTATION
70 
71 ==============================================================================
72 */
73 
74 /** class Map
75  *
76  * This really identifies a map like it is in the game
77  */
78 
Map()79 Map::Map()
80    : nrplayers_(0),
81      scenario_types_(NO_SCENARIO),
82      width_(0),
83      height_(0),
84      pathfieldmgr_(new PathfieldManager),
85      allows_seafaring_(false),
86      waterway_max_length_(0) {
87 }
88 
~Map()89 Map::~Map() {
90 	cleanup();
91 }
92 
recalc_border(const FCoords & fc)93 void Map::recalc_border(const FCoords& fc) {
94 	if (const PlayerNumber owner = fc.field->get_owned_by()) {
95 		//  A node that is owned by a player and has a neighbour that is not owned
96 		//  by that player is a border node.
97 		for (uint8_t i = 1; i <= 6; ++i) {
98 			FCoords neighbour;
99 			get_neighbour(fc, i, &neighbour);
100 			if (neighbour.field->get_owned_by() != owner) {
101 				fc.field->set_border(true);
102 				return;  //  Do not calculate further if there is a border.
103 			}
104 		}
105 	}
106 	fc.field->set_border(false);
107 }
108 
109 /*
110 ===============
111 Call this function whenever the field at fx/fy has changed in one of the ways:
112  - height has changed
113  - robust MapObject has been added or removed
114 
115 This performs the steps outlined in the comment above Map::recalc_brightness()
116 and recalcs the interactive player's overlay.
117 ===============
118 */
recalc_for_field_area(const EditorGameBase & egbase,const Area<FCoords> area)119 void Map::recalc_for_field_area(const EditorGameBase& egbase, const Area<FCoords> area) {
120 	assert(0 <= area.x);
121 	assert(area.x < width_);
122 	assert(0 <= area.y);
123 	assert(area.y < height_);
124 	assert(fields_.get() <= area.field);
125 	assert(area.field < fields_.get() + max_index());
126 
127 	{  //  First pass.
128 		MapRegion<Area<FCoords>> mr(*this, area);
129 		do {
130 			recalc_brightness(mr.location());
131 			recalc_border(mr.location());
132 			recalc_nodecaps_pass1(egbase, mr.location());
133 		} while (mr.advance(*this));
134 	}
135 
136 	{  //  Second pass.
137 		MapRegion<Area<FCoords>> mr(*this, area);
138 		do
139 			recalc_nodecaps_pass2(egbase, mr.location());
140 		while (mr.advance(*this));
141 	}
142 }
143 
144 /*
145 ===========
146 
147 this recalculates all data that needs to be recalculated.
148 This is only needed when all fields have change, this means
149 a map has been loaded or newly created or in the editor that
150 the overlays have completely changed.
151 ===========
152 */
recalc_whole_map(const EditorGameBase & egbase)153 void Map::recalc_whole_map(const EditorGameBase& egbase) {
154 	//  Post process the map in the necessary two passes to calculate
155 	//  brightness and building caps
156 	FCoords f;
157 
158 	for (int16_t y = 0; y < height_; ++y)
159 		for (int16_t x = 0; x < width_; ++x) {
160 			f = get_fcoords(Coords(x, y));
161 			uint32_t radius = 0;
162 			check_neighbour_heights(f, radius);
163 			recalc_brightness(f);
164 			recalc_border(f);
165 			recalc_nodecaps_pass1(egbase, f);
166 		}
167 
168 	for (int16_t y = 0; y < height_; ++y)
169 		for (int16_t x = 0; x < width_; ++x) {
170 			f = get_fcoords(Coords(x, y));
171 			recalc_nodecaps_pass2(egbase, f);
172 		}
173 	recalculate_allows_seafaring();
174 }
175 
recalc_default_resources(const World & world)176 void Map::recalc_default_resources(const World& world) {
177 	for (int16_t y = 0; y < height_; ++y)
178 		for (int16_t x = 0; x < width_; ++x) {
179 			FCoords f, f1;
180 			f = get_fcoords(Coords(x, y));
181 			//  only on unset nodes
182 			if (f.field->get_resources() != Widelands::kNoResource || f.field->get_resources_amount())
183 				continue;
184 			std::map<int32_t, int32_t> m;
185 			ResourceAmount amount = 0;
186 
187 			//  this node
188 			{
189 				const TerrainDescription& terr = world.terrain_descr(f.field->terrain_r());
190 				++m[terr.get_default_resource()];
191 				amount += terr.get_default_resource_amount();
192 			}
193 			{
194 				const TerrainDescription& terd = world.terrain_descr(f.field->terrain_d());
195 				++m[terd.get_default_resource()];
196 				amount += terd.get_default_resource_amount();
197 			}
198 
199 			//  If one of the neighbours is unwalkable, count its resource
200 			//  stronger
201 			//  top left neigbour
202 			get_neighbour(f, WALK_NW, &f1);
203 			{
204 				const TerrainDescription& terr = world.terrain_descr(f1.field->terrain_r());
205 				const DescriptionIndex resr = terr.get_default_resource();
206 				const ResourceAmount default_amount = terr.get_default_resource_amount();
207 				if ((terr.get_is() & TerrainDescription::Is::kUnwalkable) && default_amount > 0)
208 					m[resr] += 3;
209 				else
210 					++m[resr];
211 				amount += default_amount;
212 			}
213 			{
214 				const TerrainDescription& terd = world.terrain_descr(f1.field->terrain_d());
215 				const DescriptionIndex resd = terd.get_default_resource();
216 				const ResourceAmount default_amount = terd.get_default_resource_amount();
217 				if ((terd.get_is() & TerrainDescription::Is::kUnwalkable) && default_amount > 0)
218 					m[resd] += 3;
219 				else
220 					++m[resd];
221 				amount += default_amount;
222 			}
223 
224 			//  top right neigbour
225 			get_neighbour(f, WALK_NE, &f1);
226 			{
227 				const TerrainDescription& terd = world.terrain_descr(f1.field->terrain_d());
228 				const DescriptionIndex resd = terd.get_default_resource();
229 				const ResourceAmount default_amount = terd.get_default_resource_amount();
230 				if ((terd.get_is() & TerrainDescription::Is::kUnwalkable) && default_amount > 0)
231 					m[resd] += 3;
232 				else
233 					++m[resd];
234 				amount += default_amount;
235 			}
236 
237 			//  left neighbour
238 			get_neighbour(f, WALK_W, &f1);
239 			{
240 				const TerrainDescription& terr = world.terrain_descr(f1.field->terrain_r());
241 				const DescriptionIndex resr = terr.get_default_resource();
242 				const ResourceAmount default_amount = terr.get_default_resource_amount();
243 				if ((terr.get_is() & TerrainDescription::Is::kUnwalkable) && default_amount > 0)
244 					m[resr] += 3;
245 				else
246 					++m[resr];
247 				amount += default_amount;
248 			}
249 
250 			int32_t lv = 0;
251 			int32_t res = 0;
252 			std::map<int32_t, int32_t>::iterator i = m.begin();
253 			while (i != m.end()) {
254 				if (i->second > lv) {
255 					lv = i->second;
256 					res = i->first;
257 				}
258 				++i;
259 			}
260 			amount /= 6;
261 
262 			if (res == -1 || res == INVALID_INDEX || res == Widelands::kNoResource || !amount) {
263 				clear_resources(f);
264 			} else {
265 				initialize_resources(f, res, amount);
266 			}
267 		}
268 }
269 
count_all_conquerable_fields()270 size_t Map::count_all_conquerable_fields() {
271 	if (!valuable_fields_.empty()) {
272 		// Already calculated
273 		return valuable_fields_.size();
274 	}
275 
276 	std::set<FCoords> coords_to_check;
277 
278 	log("Collecting valuable fields ... ");
279 	ScopedTimer timer("took %ums");
280 
281 	// If we don't have the given coordinates yet, walk the map and collect conquerable fields,
282 	// initialized with the given radius around the coordinates
283 	const auto walk_starting_coords = [this, &coords_to_check](const Coords& coords, int radius) {
284 		FCoords fcoords = get_fcoords(coords);
285 
286 		// We already have these coordinates
287 		if (valuable_fields_.count(fcoords) == 1) {
288 			return;
289 		}
290 
291 		// Add starting field
292 		valuable_fields_.insert(fcoords);
293 
294 		// Add outer land coordinates around the starting field for the given radius
295 		std::unique_ptr<Widelands::HollowArea<>> hollow_area(
296 		   new Widelands::HollowArea<>(Widelands::Area<>(fcoords, radius), 2));
297 		std::unique_ptr<Widelands::MapHollowRegion<>> map_region(
298 		   new Widelands::MapHollowRegion<>(*this, *hollow_area));
299 		do {
300 			coords_to_check.insert(get_fcoords(map_region->location()));
301 		} while (map_region->advance(*this));
302 
303 		// Walk the map
304 		while (!coords_to_check.empty()) {
305 			// Get some coordinates to check
306 			const auto coords_it = coords_to_check.begin();
307 			fcoords = *coords_it;
308 
309 			// Get region according to buildcaps
310 			radius = 0;
311 			int inner_radius = 2;
312 			if ((fcoords.field->maxcaps() & BUILDCAPS_BIG) == BUILDCAPS_BIG) {
313 				radius = 9;
314 				inner_radius = 7;
315 			} else if (fcoords.field->maxcaps() & BUILDCAPS_MEDIUM) {
316 				radius = 7;
317 				inner_radius = 5;
318 			} else if (fcoords.field->maxcaps() & BUILDCAPS_SMALL) {
319 				radius = 5;
320 			}
321 
322 			// Check region and add walkable fields
323 			if (radius > 0) {
324 				hollow_area.reset(
325 				   new Widelands::HollowArea<>(Widelands::Area<>(fcoords, radius), inner_radius));
326 				map_region.reset(new Widelands::MapHollowRegion<>(*this, *hollow_area));
327 				do {
328 					fcoords = get_fcoords(map_region->location());
329 
330 					// We do the caps check first, because the comparison is faster than the container
331 					// check
332 					if ((fcoords.field->maxcaps() & MOVECAPS_WALK) &&
333 					    (valuable_fields_.count(fcoords) == 0)) {
334 						valuable_fields_.insert(fcoords);
335 						coords_to_check.insert(fcoords);
336 					}
337 				} while (map_region->advance(*this));
338 			}
339 
340 			// These coordinates are done. We do not keep track of visited coordinates that didn't make
341 			// the result, because the container insert operations are more expensive than the checks
342 			coords_to_check.erase(coords_it);
343 		}
344 	};
345 
346 	// Walk the map from the starting field of each player
347 	for (const Coords& coords : starting_pos_) {
348 		walk_starting_coords(coords, 9);
349 	}
350 
351 	// Walk the map from port spaces
352 	if (allows_seafaring()) {
353 		for (const Coords& coords : get_port_spaces()) {
354 			walk_starting_coords(coords, 5);
355 		}
356 	}
357 
358 	log("%" PRIuS " found ... ", valuable_fields_.size());
359 	return valuable_fields_.size();
360 }
361 
count_all_fields_excluding_caps(NodeCaps caps)362 size_t Map::count_all_fields_excluding_caps(NodeCaps caps) {
363 	if (!valuable_fields_.empty()) {
364 		// Already calculated
365 		return valuable_fields_.size();
366 	}
367 
368 	log("Collecting valuable fields ... ");
369 	ScopedTimer timer("took %ums");
370 
371 	for (MapIndex i = 0; i < max_index(); ++i) {
372 		Field& field = fields_[i];
373 		if (!(field.nodecaps() & caps)) {
374 			valuable_fields_.insert(get_fcoords(field));
375 		}
376 	}
377 
378 	log("%" PRIuS " found ... ", valuable_fields_.size());
379 	return valuable_fields_.size();
380 }
381 
382 std::map<PlayerNumber, size_t>
count_owned_valuable_fields(const std::string & immovable_attribute) const383 Map::count_owned_valuable_fields(const std::string& immovable_attribute) const {
384 	std::map<PlayerNumber, size_t> result;
385 	const bool use_attribute = !immovable_attribute.empty();
386 	const uint32_t attribute_id =
387 	   use_attribute ? MapObjectDescr::get_attribute_id(immovable_attribute) : 0U;
388 	for (const FCoords& fcoords : valuable_fields_) {
389 		if (use_attribute) {
390 			const BaseImmovable* imm = fcoords.field->get_immovable();
391 			if (imm != nullptr && imm->has_attribute(attribute_id)) {
392 				++result[fcoords.field->get_owned_by()];
393 			}
394 		} else {
395 			++result[fcoords.field->get_owned_by()];
396 		}
397 	}
398 	return result;
399 }
400 
401 /*
402 ===============
403 remove your world, remove your data
404 go back to your initial state
405 ===============
406 */
cleanup()407 void Map::cleanup() {
408 	nrplayers_ = 0;
409 	width_ = height_ = 0;
410 
411 	fields_.reset();
412 
413 	starting_pos_.clear();
414 	scenario_tribes_.clear();
415 	scenario_names_.clear();
416 	scenario_ais_.clear();
417 	scenario_closeables_.clear();
418 
419 	tags_.clear();
420 	hint_ = std::string();
421 	background_ = std::string();
422 
423 	objectives_.clear();
424 	port_spaces_.clear();
425 	allows_seafaring_ = false;
426 
427 	// TODO(meitis): should be done here ... but WidelandsMapLoader::preload_map calls
428 	// this cleanup AFTER assigning filesystem_ in WidelandsMapLoader::WidelandsMapLoader
429 	// ... so we can't do it here :/
430 	// filesystem_.reset(nullptr);
431 }
432 
433 /*
434 ===========
435 creates an empty map without name with
436 the given data
437 ===========
438 */
create_empty_map(const EditorGameBase & egbase,uint32_t const w,uint32_t const h,const Widelands::DescriptionIndex default_terrain,const std::string & name,const std::string & author,const std::string & description)439 void Map::create_empty_map(const EditorGameBase& egbase,
440                            uint32_t const w,
441                            uint32_t const h,
442                            const Widelands::DescriptionIndex default_terrain,
443                            const std::string& name,
444                            const std::string& author,
445                            const std::string& description) {
446 	set_size(w, h);
447 	set_name(name);
448 	set_author(author);
449 	set_description(description);
450 	set_nrplayers(0);
451 	{
452 		Field::Terrains default_terrains;
453 		default_terrains.d = default_terrain;
454 		default_terrains.r = default_terrain;
455 		for (int16_t y = 0; y < height_; ++y) {
456 			for (int16_t x = 0; x < width_; ++x) {
457 				auto f = get_fcoords(Coords(x, y));
458 				f.field->set_height(10);
459 				f.field->set_terrains(default_terrains);
460 				clear_resources(f);
461 			}
462 		}
463 	}
464 	recalc_whole_map(egbase);
465 
466 	filesystem_.reset(nullptr);
467 }
468 
469 // Made this a separate function to reduce compiler warnings
470 template <typename T = Field>
clear_array(std::unique_ptr<T[]> * array,uint32_t size)471 static inline void clear_array(std::unique_ptr<T[]>* array, uint32_t size) {
472 	memset(array->get(), 0, sizeof(T) * size);
473 }
474 
set_origin(const Coords & new_origin)475 void Map::set_origin(const Coords& new_origin) {
476 	assert(0 <= new_origin.x);
477 	assert(new_origin.x < width_);
478 	assert(0 <= new_origin.y);
479 	assert(new_origin.y < height_);
480 
481 	const size_t field_size = width_ * height_;
482 
483 	for (uint8_t i = get_nrplayers(); i;) {
484 		starting_pos_[--i].reorigin(new_origin, extent());
485 	}
486 
487 	std::unique_ptr<Field[]> new_field_order(new Field[field_size]);
488 	clear_array<>(&new_field_order, field_size);
489 
490 	// Rearrange The fields
491 	// NOTE because of the triangle design, we have to take special care of cases
492 	// NOTE where y is changed by an odd number
493 	bool yisodd = (new_origin.y % 2) != 0;
494 	for (Coords c(Coords(0, 0)); c.y < height_; ++c.y) {
495 		bool cyisodd = (c.y % 2) != 0;
496 		for (c.x = 0; c.x < width_; ++c.x) {
497 			Coords temp;
498 			if (yisodd && cyisodd) {
499 				temp = Coords(c.x + new_origin.x + 1, c.y + new_origin.y);
500 			} else {
501 				temp = Coords(c.x + new_origin.x, c.y + new_origin.y);
502 			}
503 			normalize_coords(temp);
504 			new_field_order[get_index(c, width_)] = operator[](temp);
505 		}
506 	}
507 	// Now that we restructured the fields, we just overwrite the old order
508 	for (size_t ind = 0; ind < field_size; ind++) {
509 		fields_[ind] = new_field_order[ind];
510 	}
511 
512 	//  Inform immovables and bobs about their new coordinates.
513 	for (FCoords c(Coords(0, 0), fields_.get()); c.y < height_; ++c.y) {
514 		for (c.x = 0; c.x < width_; ++c.x, ++c.field) {
515 			assert(c.field == &operator[](c));
516 			if (upcast(Immovable, immovable, c.field->get_immovable())) {
517 				immovable->position_ = c;
518 			}
519 			bool is_first_bob = true;
520 			for (Bob* bob = c.field->get_first_bob(); bob; bob = bob->get_next_bob()) {
521 				bob->position_.x = c.x;
522 				bob->position_.y = c.y;
523 				bob->position_.field = c.field;
524 				if (is_first_bob) {
525 					bob->linkpprev_ = &c.field->bobs;
526 					is_first_bob = false;
527 				}
528 			}
529 		}
530 	}
531 
532 	// Take care of port spaces
533 	PortSpacesSet new_port_spaces;
534 	for (PortSpacesSet::iterator it = port_spaces_.begin(); it != port_spaces_.end(); ++it) {
535 		Coords temp;
536 		if (yisodd && ((it->y % 2) == 0)) {
537 			temp = Coords(it->x - new_origin.x - 1, it->y - new_origin.y);
538 		} else {
539 			temp = Coords(it->x - new_origin.x, it->y - new_origin.y);
540 		}
541 		normalize_coords(temp);
542 		new_port_spaces.insert(temp);
543 	}
544 	port_spaces_ = new_port_spaces;
545 	log("Map origin was shifted by (%d, %d)\n", new_origin.x, new_origin.y);
546 }
547 
548 // Helper function for resize()
549 constexpr int32_t kInvalidCoords = -1;
resize_coordinates_conversion(const int32_t old_coord,const int32_t split_point,const int32_t old_dimension,const int32_t new_dimension)550 static inline int32_t resize_coordinates_conversion(const int32_t old_coord,
551                                                     const int32_t split_point,
552                                                     const int32_t old_dimension,
553                                                     const int32_t new_dimension) {
554 	if (new_dimension == old_dimension) {
555 		// trivial
556 		return old_coord;
557 	} else if (new_dimension > old_dimension) {
558 		// enlarge
559 		return old_coord > split_point ? old_coord + new_dimension - old_dimension : old_coord;
560 	} else if (split_point > new_dimension) {
561 		// shrink, origin deleted
562 		return (old_coord >= split_point || old_coord < split_point - new_dimension) ?
563 		          kInvalidCoords :
564 		          old_coord - split_point + new_dimension;
565 	} else {
566 		// shrink, origin preserved
567 		return old_coord < split_point ? old_coord :
568 		                                 old_coord < split_point + old_dimension - new_dimension ?
569 		                                 kInvalidCoords :
570 		                                 old_coord + new_dimension - old_dimension;
571 	}
572 }
573 
resize(EditorGameBase & egbase,const Coords split,const int32_t w,const int32_t h)574 void Map::resize(EditorGameBase& egbase, const Coords split, const int32_t w, const int32_t h) {
575 	if (w == width_ && h == height_) {
576 		return;
577 	}
578 
579 	// Generate the new fields. Does not modify the actual map yet.
580 
581 	std::unique_ptr<Field[]> new_fields(new Field[w * h]);
582 	clear_array<>(&new_fields, w * h);
583 	std::unique_ptr<bool[]> was_preserved(new bool[width_ * height_]);
584 	std::unique_ptr<bool[]> was_created(new bool[w * h]);
585 	clear_array<bool>(&was_preserved, width_ * height_);
586 	clear_array<bool>(&was_created, w * h);
587 
588 	for (int16_t x = 0; x < width_; ++x) {
589 		for (int16_t y = 0; y < height_; ++y) {
590 			const int16_t new_x = resize_coordinates_conversion(x, split.x, width_, w);
591 			const int16_t new_y = resize_coordinates_conversion(y, split.y, height_, h);
592 			assert((new_x >= 0 && new_x < w) || new_x == kInvalidCoords);
593 			assert((new_y >= 0 && new_y < h) || new_y == kInvalidCoords);
594 			if (new_x != kInvalidCoords && new_y != kInvalidCoords) {
595 				Coords old_coords(x, y);
596 				Coords new_coords(new_x, new_y);
597 				const MapIndex old_index = get_index(old_coords);
598 				const MapIndex new_index = get_index(new_coords, w);
599 
600 				assert(!was_preserved[old_index]);
601 				was_preserved[old_index] = true;
602 
603 				assert(!was_created[new_index]);
604 				was_created[new_index] = true;
605 
606 				new_fields[new_index] = (*this)[old_coords];
607 			}
608 		}
609 	}
610 	Field::Terrains default_terrains;
611 	default_terrains.r = 0;
612 	default_terrains.d = 0;
613 	for (MapIndex index = w * h; index; --index) {
614 		if (!was_created[index - 1]) {
615 			Field& field = new_fields[index - 1];
616 			field.set_height(10);
617 			field.set_terrains(default_terrains);
618 		}
619 	}
620 
621 	// Now modify our starting positions and port spaces
622 
623 	for (Coords& c : starting_pos_) {
624 		if (c) {  // only if set (c != Coords::null())
625 			const int16_t x = resize_coordinates_conversion(c.x, split.x, width_, w);
626 			const int16_t y = resize_coordinates_conversion(c.y, split.y, height_, h);
627 			assert((x >= 0 && x < w) || x == kInvalidCoords);
628 			assert((y >= 0 && y < h) || y == kInvalidCoords);
629 			c = ((x == kInvalidCoords || y == kInvalidCoords) ? Coords::null() : Coords(x, y));
630 		}
631 	}
632 
633 	{
634 		PortSpacesSet new_port_spaces;
635 		for (const Coords& c : port_spaces_) {
636 			const int16_t x = resize_coordinates_conversion(c.x, split.x, width_, w);
637 			const int16_t y = resize_coordinates_conversion(c.y, split.y, height_, h);
638 			assert((x >= 0 && x < w) || x == kInvalidCoords);
639 			assert((y >= 0 && y < h) || y == kInvalidCoords);
640 			if (x != kInvalidCoords && y != kInvalidCoords) {
641 				new_port_spaces.insert(Coords(x, y));
642 			}
643 		}
644 		port_spaces_ = new_port_spaces;
645 	}
646 
647 	// Delete map objects whose position will be deleted.
648 	for (int16_t x = 0; x < width_; ++x) {
649 		for (int16_t y = 0; y < height_; ++y) {
650 			Coords c(x, y);
651 			if (!was_preserved[get_index(c)]) {
652 				Field& f = (*this)[c];
653 				if (upcast(Immovable, i, f.get_immovable())) {
654 					i->remove(egbase);
655 				}
656 				while (Bob* b = f.get_first_bob()) {
657 					b->remove(egbase);
658 				}
659 			}
660 		}
661 	}
662 
663 	// Now replace all existing fields with the new values.
664 
665 	width_ = w;
666 	height_ = h;
667 	fields_.reset(new_fields.release());
668 
669 	// Always call allocate_player_maps() while changing the map's size.
670 	// Forgetting to do so will result in random crashes.
671 	egbase.allocate_player_maps();
672 	// Recalculate nodecaps etc
673 	recalc_whole_map(egbase);
674 
675 	// MapObjects keep pointers to the Field they are located on. These pointers
676 	// need updating because the memory addresses of all fields changed now.
677 	for (int16_t x = 0; x < width_; ++x) {
678 		for (int16_t y = 0; y < height_; ++y) {
679 			Field& f = (*this)[Coords(x, y)];
680 			FCoords fc(Coords(x, y), &f);
681 			if (upcast(Immovable, imm, f.get_immovable())) {
682 				imm->position_ = fc;
683 			}
684 			if (Bob* b = f.get_first_bob()) {
685 				b->linkpprev_ = &f.bobs;
686 			}
687 			for (Bob* b = f.get_first_bob(); b; b = b->get_next_bob()) {
688 				b->position_ = fc;
689 			}
690 		}
691 	}
692 
693 	log("Map was resized to %d×%d\n", width_, height_);
694 }
695 
dump_state(const EditorGameBase &) const696 ResizeHistory Map::dump_state(const EditorGameBase&) const {
697 	ResizeHistory rh;
698 	rh.size.w = width_;
699 	rh.size.h = height_;
700 	rh.port_spaces = port_spaces_;
701 	rh.starting_positions = starting_pos_;
702 	for (MapIndex i = max_index(); i; --i) {
703 		rh.fields.push_back(FieldData(operator[](i - 1)));
704 	}
705 	return rh;
706 }
707 
set_to(EditorGameBase & egbase,ResizeHistory rh)708 void Map::set_to(EditorGameBase& egbase, ResizeHistory rh) {
709 	std::list<FieldData> backup = rh.fields;
710 
711 	// Delete all map objects
712 	for (int16_t x = 0; x < width_; ++x) {
713 		for (int16_t y = 0; y < height_; ++y) {
714 			Field& f = operator[](Coords(x, y));
715 			if (upcast(Immovable, i, f.get_immovable())) {
716 				i->remove(egbase);
717 			}
718 			while (Bob* b = f.get_first_bob()) {
719 				b->remove(egbase);
720 			}
721 		}
722 	}
723 
724 	// Reset the fields to blank
725 	width_ = rh.size.w;
726 	height_ = rh.size.h;
727 	fields_.reset(new Field[width_ * height_]);
728 	egbase.allocate_player_maps();
729 
730 	// Overwrite starting locations and port spaces
731 	port_spaces_ = rh.port_spaces;
732 	starting_pos_ = rh.starting_positions;
733 
734 	// First pass: Initialize all fields with the saved basic data
735 	for (MapIndex i = max_index(); i; --i) {
736 		const FieldData& fd = rh.fields.front();
737 		Field& f = fields_[i - 1];
738 		f.set_terrains(fd.terrains);
739 		f.set_height(fd.height);
740 		f.resources = fd.resources;
741 		f.initial_res_amount = fd.resource_amount;
742 		f.res_amount = fd.resource_amount;
743 		rh.fields.pop_front();
744 	}
745 	// Calculate nodecaps and stuff
746 	recalc_whole_map(egbase);
747 
748 	// Second pass: Re-create desired map objects
749 	for (MapIndex i = max_index(); i; --i) {
750 		const FieldData& fd = backup.front();
751 		FCoords fc = get_fcoords(operator[](i - 1));
752 		if (!fd.immovable.empty()) {
753 			egbase.create_immovable_with_name(
754 			   fc, fd.immovable, Widelands::MapObjectDescr::OwnerType::kWorld, nullptr, nullptr);
755 		}
756 		for (const std::string& bob : fd.bobs) {
757 			egbase.create_critter(fc, bob);
758 		}
759 		backup.pop_front();
760 	}
761 }
762 
763 /*
764 ===============
765 Set the size of the map. This should only happen once during initial load.
766 ===============
767 */
set_size(const uint32_t w,const uint32_t h)768 void Map::set_size(const uint32_t w, const uint32_t h) {
769 	assert(!fields_);
770 
771 	width_ = w;
772 	height_ = h;
773 
774 	const uint32_t field_size = w * h;
775 
776 	fields_.reset(new Field[field_size]);
777 	clear_array<>(&fields_, field_size);
778 
779 	pathfieldmgr_->set_size(field_size);
780 }
781 
needs_widelands_version_after() const782 int Map::needs_widelands_version_after() const {
783 	return map_version_.needs_widelands_version_after;
784 }
785 
calculate_needs_widelands_version_after(bool is_post_one_world)786 void Map::calculate_needs_widelands_version_after(bool is_post_one_world) {
787 	if (map_version_.needs_widelands_version_after == 0) {
788 		if (nrplayers_ > 8) {
789 			// We introduced support for 16 players after Build 19
790 			map_version_.needs_widelands_version_after = 19;
791 		} else if (is_post_one_world) {
792 			// We merged the worlds in the engine after Build 18
793 			map_version_.needs_widelands_version_after = 18;
794 		}
795 	}
796 }
797 
798 /*
799  * Getter and setter for the highest permitted length of a waterway on this map.
800  * A value of 0 or 1 means no waterways can be built.
801  */
get_waterway_max_length() const802 uint32_t Map::get_waterway_max_length() const {
803 	return waterway_max_length_;
804 }
805 
set_waterway_max_length(uint32_t max_length)806 void Map::set_waterway_max_length(uint32_t max_length) {
807 	waterway_max_length_ = max_length;
808 }
809 
810 /*
811  * The scenario get/set functions
812  */
get_scenario_player_tribe(const PlayerNumber p) const813 const std::string& Map::get_scenario_player_tribe(const PlayerNumber p) const {
814 	assert(scenario_tribes_.size() == get_nrplayers());
815 	assert(p);
816 	assert(p <= get_nrplayers());
817 	return scenario_tribes_[p - 1];
818 }
819 
get_scenario_player_name(const PlayerNumber p) const820 const std::string& Map::get_scenario_player_name(const PlayerNumber p) const {
821 	assert(scenario_names_.size() == get_nrplayers());
822 	assert(p);
823 	assert(p <= get_nrplayers());
824 	return scenario_names_[p - 1];
825 }
826 
get_scenario_player_ai(const PlayerNumber p) const827 const std::string& Map::get_scenario_player_ai(const PlayerNumber p) const {
828 	assert(scenario_ais_.size() == get_nrplayers());
829 	assert(p);
830 	assert(p <= get_nrplayers());
831 	return scenario_ais_[p - 1];
832 }
833 
get_scenario_player_closeable(const PlayerNumber p) const834 bool Map::get_scenario_player_closeable(const PlayerNumber p) const {
835 	assert(scenario_closeables_.size() == get_nrplayers());
836 	assert(p);
837 	assert(p <= get_nrplayers());
838 	return scenario_closeables_[p - 1];
839 }
840 
swap_filesystem(std::unique_ptr<FileSystem> & fs)841 void Map::swap_filesystem(std::unique_ptr<FileSystem>& fs) {
842 	filesystem_.swap(fs);
843 }
844 
reset_filesystem()845 void Map::reset_filesystem() {
846 	filesystem_.reset();
847 }
848 
filesystem() const849 FileSystem* Map::filesystem() const {
850 	return filesystem_.get();
851 }
852 
set_scenario_player_tribe(PlayerNumber const p,const std::string & tribename)853 void Map::set_scenario_player_tribe(PlayerNumber const p, const std::string& tribename) {
854 	assert(p);
855 	assert(p <= get_nrplayers());
856 	scenario_tribes_.resize(get_nrplayers());
857 	scenario_tribes_[p - 1] = tribename;
858 }
859 
set_scenario_player_name(PlayerNumber const p,const std::string & playername)860 void Map::set_scenario_player_name(PlayerNumber const p, const std::string& playername) {
861 	assert(p);
862 	assert(p <= get_nrplayers());
863 	scenario_names_.resize(get_nrplayers());
864 	scenario_names_[p - 1] = playername;
865 }
866 
set_scenario_player_ai(PlayerNumber const p,const std::string & ainame)867 void Map::set_scenario_player_ai(PlayerNumber const p, const std::string& ainame) {
868 	assert(p);
869 	assert(p <= get_nrplayers());
870 	scenario_ais_.resize(get_nrplayers());
871 	scenario_ais_[p - 1] = ainame;
872 }
873 
set_scenario_player_closeable(PlayerNumber const p,bool closeable)874 void Map::set_scenario_player_closeable(PlayerNumber const p, bool closeable) {
875 	assert(p);
876 	assert(p <= get_nrplayers());
877 	scenario_closeables_.resize(get_nrplayers());
878 	scenario_closeables_[p - 1] = closeable;
879 }
880 
881 /*
882 ===============
883 Change the number of players the map supports.
884 Could happen multiple times in the map editor.
885 ===============
886 */
set_nrplayers(PlayerNumber const nrplayers)887 void Map::set_nrplayers(PlayerNumber const nrplayers) {
888 	if (!nrplayers) {
889 		nrplayers_ = 0;
890 		return;
891 	}
892 
893 	starting_pos_.resize(nrplayers, Coords::null());
894 	scenario_tribes_.resize(nrplayers);
895 	scenario_ais_.resize(nrplayers);
896 	scenario_closeables_.resize(nrplayers);
897 	scenario_names_.resize(nrplayers);
898 	scenario_tribes_.resize(nrplayers);
899 
900 	nrplayers_ = nrplayers;  // in case the number players got less
901 }
902 
903 /*
904 ===============
905 Set the starting coordinates of a player
906 ===============
907 */
set_starting_pos(PlayerNumber const plnum,const Coords & c)908 void Map::set_starting_pos(PlayerNumber const plnum, const Coords& c) {
909 	assert(1 <= plnum && plnum <= get_nrplayers());
910 	starting_pos_[plnum - 1] = c;
911 }
912 
set_filename(const std::string & filename)913 void Map::set_filename(const std::string& filename) {
914 	filename_ = filename;
915 }
916 
set_author(const std::string & author)917 void Map::set_author(const std::string& author) {
918 	author_ = author;
919 }
920 
set_name(const std::string & name)921 void Map::set_name(const std::string& name) {
922 	name_ = name;
923 }
924 
set_description(const std::string & description)925 void Map::set_description(const std::string& description) {
926 	description_ = description;
927 }
928 
set_hint(const std::string & hint)929 void Map::set_hint(const std::string& hint) {
930 	hint_ = hint;
931 }
932 
set_background(const std::string & image_path)933 void Map::set_background(const std::string& image_path) {
934 	if (image_path.empty())
935 		background_.clear();
936 	else
937 		background_ = image_path;
938 }
939 
add_tag(const std::string & tag)940 void Map::add_tag(const std::string& tag) {
941 	tags_.insert(tag);
942 }
943 
delete_tag(const std::string & tag)944 void Map::delete_tag(const std::string& tag) {
945 	if (has_tag(tag)) {
946 		tags_.erase(tags_.find(tag));
947 	}
948 }
949 
get_max_nodecaps(const EditorGameBase & egbase,const FCoords & fc) const950 NodeCaps Map::get_max_nodecaps(const EditorGameBase& egbase, const FCoords& fc) const {
951 	NodeCaps max_caps = calc_nodecaps_pass1(egbase, fc, false);
952 	max_caps = calc_nodecaps_pass2(egbase, fc, false, max_caps);
953 	return static_cast<NodeCaps>(max_caps);
954 }
955 
956 /// \returns the immovable at the given coordinate
get_immovable(const Coords & coord) const957 BaseImmovable* Map::get_immovable(const Coords& coord) const {
958 	return operator[](coord).get_immovable();
959 }
960 
961 /*
962 ===============
963 Call the functor for every field that can be reached from coord without moving
964 outside the given radius.
965 
966 Functor is of the form: functor(Map*, FCoords)
967 ===============
968 */
969 template <typename functorT>
find_reachable(const EditorGameBase & egbase,const Area<FCoords> & area,const CheckStep & checkstep,functorT & functor) const970 void Map::find_reachable(const EditorGameBase& egbase,
971                          const Area<FCoords>& area,
972                          const CheckStep& checkstep,
973                          functorT& functor) const {
974 	std::vector<Coords> queue;
975 	std::shared_ptr<Pathfields> pathfields = pathfieldmgr_->allocate();
976 
977 	queue.push_back(area);
978 
979 	while (queue.size()) {
980 		// Pop the last ware from the queue
981 		FCoords const cur = get_fcoords(*queue.rbegin());
982 		queue.pop_back();
983 		Pathfield& curpf = pathfields->fields[cur.field - fields_.get()];
984 
985 		//  handle this node
986 		functor(egbase, cur);
987 		curpf.cycle = pathfields->cycle;
988 
989 		// Get neighbours
990 		for (Direction dir = 1; dir <= 6; ++dir) {
991 			FCoords neighb;
992 
993 			get_neighbour(cur, dir, &neighb);
994 
995 			if  //  node not already handled?
996 			   (pathfields->fields[neighb.field - fields_.get()].cycle != pathfields->cycle &&
997 			    //  node within the radius?
998 			    calc_distance(area, neighb) <= area.radius &&
999 			    //  allowed to move onto this node?
1000 			    checkstep.allowed(*this, cur, neighb, dir,
1001 			                      cur == area ? CheckStep::stepFirst : CheckStep::stepNormal))
1002 				queue.push_back(neighb);
1003 		}
1004 	}
1005 }
1006 
1007 /*
1008 ===============
1009 Call the functor for every field within the given radius.
1010 
1011 Functor is of the form: functor(Map &, FCoords)
1012 ===============
1013 */
1014 template <typename functorT>
find(const EditorGameBase & egbase,const Area<FCoords> & area,functorT & functor) const1015 void Map::find(const EditorGameBase& egbase, const Area<FCoords>& area, functorT& functor) const {
1016 	MapRegion<Area<FCoords>> mr(*this, area);
1017 	do
1018 		functor(egbase, mr.location());
1019 	while (mr.advance(*this));
1020 }
1021 
1022 /*
1023 ===============
1024 FindBobsCallback
1025 
1026 The actual logic behind find_bobs and find_reachable_bobs.
1027 ===============
1028 */
1029 struct FindBobsCallback {
FindBobsCallbackWidelands::FindBobsCallback1030 	FindBobsCallback(std::vector<Bob*>* const list, const FindBob& functor)
1031 	   : list_(list), functor_(functor), found_(0) {
1032 	}
1033 
operator ()Widelands::FindBobsCallback1034 	void operator()(const EditorGameBase&, const FCoords& cur) {
1035 		for (Bob* bob = cur.field->get_first_bob(); bob; bob = bob->get_next_bob()) {
1036 			if (list_ && std::find(list_->begin(), list_->end(), bob) != list_->end())
1037 				continue;
1038 
1039 			if (functor_.accept(bob)) {
1040 				if (list_)
1041 					list_->push_back(bob);
1042 
1043 				++found_;
1044 			}
1045 		}
1046 	}
1047 
1048 	std::vector<Bob*>* list_;
1049 	const FindBob& functor_;
1050 	uint32_t found_;
1051 };
1052 
1053 /*
1054 ===============
1055 Find Bobs in the given area. Only finds objects for which
1056 functor.accept() returns true (the default functor always returns true)
1057 If list is non-zero, pointers to the relevant objects will be stored in
1058 the list.
1059 
1060 Returns the number of objects found.
1061 ===============
1062 */
find_bobs(const EditorGameBase & egbase,Area<FCoords> const area,std::vector<Bob * > * const list,const FindBob & functor) const1063 uint32_t Map::find_bobs(const EditorGameBase& egbase,
1064                         Area<FCoords> const area,
1065                         std::vector<Bob*>* const list,
1066                         const FindBob& functor) const {
1067 	FindBobsCallback cb(list, functor);
1068 
1069 	find(egbase, area, cb);
1070 
1071 	return cb.found_;
1072 }
1073 
1074 /*
1075 ===============
1076 Find Bobs that are reachable by moving within the given radius (also see
1077 find_reachable()).
1078 Only finds objects for which functor.accept() returns true (the default functor
1079 always returns true).
1080 If list is non-zero, pointers to the relevant objects will be stored in
1081 the list.
1082 
1083 Returns the number of objects found.
1084 ===============
1085 */
find_reachable_bobs(const EditorGameBase & egbase,Area<FCoords> const area,std::vector<Bob * > * const list,const CheckStep & checkstep,const FindBob & functor) const1086 uint32_t Map::find_reachable_bobs(const EditorGameBase& egbase,
1087                                   Area<FCoords> const area,
1088                                   std::vector<Bob*>* const list,
1089                                   const CheckStep& checkstep,
1090                                   const FindBob& functor) const {
1091 	FindBobsCallback cb(list, functor);
1092 
1093 	find_reachable(egbase, area, checkstep, cb);
1094 
1095 	return cb.found_;
1096 }
1097 
1098 /*
1099 ===============
1100 FindImmovablesCallback
1101 
1102 The actual logic behind find_immovables and find_reachable_immovables.
1103 ===============
1104 */
1105 struct FindImmovablesCallback {
FindImmovablesCallbackWidelands::FindImmovablesCallback1106 	FindImmovablesCallback(std::vector<ImmovableFound>* const list, const FindImmovable& functor)
1107 	   : list_(list), functor_(functor), found_(0) {
1108 	}
1109 
operator ()Widelands::FindImmovablesCallback1110 	void operator()(const EditorGameBase&, const FCoords& cur) {
1111 		BaseImmovable* const imm = cur.field->get_immovable();
1112 
1113 		if (!imm)
1114 			return;
1115 
1116 		if (functor_.accept(*imm)) {
1117 			if (list_) {
1118 				ImmovableFound imf;
1119 				imf.object = imm;
1120 				imf.coords = cur;
1121 				list_->push_back(imf);
1122 			}
1123 
1124 			++found_;
1125 		}
1126 	}
1127 
1128 	std::vector<ImmovableFound>* list_;
1129 	const FindImmovable& functor_;
1130 	uint32_t found_;
1131 };
1132 
1133 /*
1134 ===============
1135 Find all immovables in the given area for which functor returns true
1136 (the default functor always returns true).
1137 Returns true if an immovable has been found.
1138 If list is not 0, found immovables are stored in list.
1139 ===============
1140 */
find_immovables(const EditorGameBase & egbase,Area<FCoords> const area,std::vector<ImmovableFound> * const list,const FindImmovable & functor) const1141 uint32_t Map::find_immovables(const EditorGameBase& egbase,
1142                               Area<FCoords> const area,
1143                               std::vector<ImmovableFound>* const list,
1144                               const FindImmovable& functor) const {
1145 	FindImmovablesCallback cb(list, functor);
1146 
1147 	find(egbase, area, cb);
1148 
1149 	return cb.found_;
1150 }
1151 
1152 /*
1153 ===============
1154 Find all immovables reachable by moving in the given radius (see
1155 find_reachable()).
1156 Return immovables for which functor returns true (the default functor
1157 always returns true).
1158 If list is not 0, found immovables are stored in list.
1159 Returns the number of immovables we found.
1160 ===============
1161 */
find_reachable_immovables(const EditorGameBase & egbase,Area<FCoords> const area,std::vector<ImmovableFound> * const list,const CheckStep & checkstep,const FindImmovable & functor) const1162 uint32_t Map::find_reachable_immovables(const EditorGameBase& egbase,
1163                                         Area<FCoords> const area,
1164                                         std::vector<ImmovableFound>* const list,
1165                                         const CheckStep& checkstep,
1166                                         const FindImmovable& functor) const {
1167 	FindImmovablesCallback cb(list, functor);
1168 
1169 	find_reachable(egbase, area, checkstep, cb);
1170 
1171 	return cb.found_;
1172 }
1173 
1174 /**
1175  * Find all immovables that are reachable without moving out of the
1176  * given area with the additional constraints given by checkstep,
1177  * and store them uniquely in a list.
1178  *
1179  * \return the number of immovables found.
1180  */
find_reachable_immovables_unique(const EditorGameBase & egbase,const Area<FCoords> area,std::vector<BaseImmovable * > & list,const CheckStep & checkstep,const FindImmovable & functor) const1181 uint32_t Map::find_reachable_immovables_unique(const EditorGameBase& egbase,
1182                                                const Area<FCoords> area,
1183                                                std::vector<BaseImmovable*>& list,
1184                                                const CheckStep& checkstep,
1185                                                const FindImmovable& functor) const {
1186 	std::vector<ImmovableFound> duplist;
1187 	FindImmovablesCallback cb(&duplist, find_immovable_always_true());
1188 
1189 	find_reachable(egbase, area, checkstep, cb);
1190 
1191 	for (ImmovableFound& imm_found : duplist) {
1192 		BaseImmovable& obj = *imm_found.object;
1193 		if (std::find(list.begin(), list.end(), &obj) == list.end()) {
1194 			if (functor.accept(obj)) {
1195 				list.push_back(&obj);
1196 			}
1197 		}
1198 	}
1199 
1200 	return list.size();
1201 }
1202 
1203 /*
1204 ===============
1205 FindNodesCallback
1206 
1207 The actual logic behind find_fields and find_reachable_fields.
1208 ===============
1209 */
1210 struct FindNodesCallback {
FindNodesCallbackWidelands::FindNodesCallback1211 	FindNodesCallback(std::vector<Coords>* const list, const FindNode& functor)
1212 	   : list_(list), functor_(functor), found_(0) {
1213 	}
1214 
operator ()Widelands::FindNodesCallback1215 	void operator()(const EditorGameBase& egbase, const FCoords& cur) {
1216 		if (functor_.accept(egbase, cur)) {
1217 			if (list_)
1218 				list_->push_back(cur);
1219 
1220 			++found_;
1221 		}
1222 	}
1223 
1224 	std::vector<Coords>* list_;
1225 	const FindNode& functor_;
1226 	uint32_t found_;
1227 };
1228 
1229 /*
1230 ===============
1231 Fills in a list of coordinates of fields within the given area that functor
1232 accepts.
1233 Returns the number of matching fields.
1234 
1235 Note that list can be 0.
1236 ===============
1237 */
find_fields(const EditorGameBase & egbase,Area<FCoords> const area,std::vector<Coords> * list,const FindNode & functor) const1238 uint32_t Map::find_fields(const EditorGameBase& egbase,
1239                           Area<FCoords> const area,
1240                           std::vector<Coords>* list,
1241                           const FindNode& functor) const {
1242 	FindNodesCallback cb(list, functor);
1243 
1244 	find(egbase, area, cb);
1245 
1246 	return cb.found_;
1247 }
1248 
1249 /*
1250 ===============
1251 Fills in a list of coordinates of fields reachable by walking within the given
1252 radius that functor accepts.
1253 Returns the number of matching fields.
1254 
1255 Note that list can be 0.
1256 ===============
1257 */
find_reachable_fields(const EditorGameBase & egbase,Area<FCoords> const area,std::vector<Coords> * list,const CheckStep & checkstep,const FindNode & functor) const1258 uint32_t Map::find_reachable_fields(const EditorGameBase& egbase,
1259                                     Area<FCoords> const area,
1260                                     std::vector<Coords>* list,
1261                                     const CheckStep& checkstep,
1262                                     const FindNode& functor) const {
1263 	FindNodesCallback cb(list, functor);
1264 
1265 	find_reachable(egbase, area, checkstep, cb);
1266 
1267 	return cb.found_;
1268 }
1269 
1270 /*
1271 Node attribute recalculation passes
1272 ------------------------------------
1273 
1274 Some events can change the map in a way that run-time calculated attributes
1275 (Field::brightness and Field::caps) need to be recalculated.
1276 
1277 These events include:
1278 - change of height (e.g. by planing)
1279 - change of terrain (in the editor)
1280 - insertion of a "robust" MapObject
1281 - removal of a "robust" MapObject
1282 
1283 All these events can change the passability, buildability, etc. of fields
1284 with a radius of two fields. This means that you must build a list of the
1285 directly affected field and all fields that can be reached in two steps.
1286 
1287 You must then perform the following operations:
1288 1. Call recalc_brightness() and recalc_nodecaps_pass1() on all nodes
1289 2. Call recalc_nodecaps_pass2() on all fields
1290 
1291 Note: it is possible to leave out recalc_brightness() unless the height has
1292 been changed.
1293 
1294 The Field::caps calculation is split into more passes because of inter-field
1295 dependencies.
1296 */
1297 
1298 /*
1299 ===============
1300 Fetch the slopes to neighbours and call the actual logic in Field
1301 ===============
1302 */
recalc_brightness(const FCoords & f)1303 void Map::recalc_brightness(const FCoords& f) {
1304 	int32_t left, right, top_left, top_right, bottom_left, bottom_right;
1305 	Field::Height const height = f.field->get_height();
1306 
1307 	{
1308 		FCoords neighbour;
1309 		get_ln(f, &neighbour);
1310 		left = height - neighbour.field->get_height();
1311 	}
1312 	{
1313 		FCoords neighbour;
1314 		get_rn(f, &neighbour);
1315 		right = height - neighbour.field->get_height();
1316 	}
1317 	{
1318 		FCoords neighbour;
1319 		get_tln(f, &neighbour);
1320 		top_left = height - neighbour.field->get_height();
1321 		get_rn(neighbour, &neighbour);
1322 		top_right = height - neighbour.field->get_height();
1323 	}
1324 	{
1325 		FCoords neighbour;
1326 		get_bln(f, &neighbour);
1327 		bottom_left = height - neighbour.field->get_height();
1328 		get_rn(neighbour, &neighbour);
1329 		bottom_right = height - neighbour.field->get_height();
1330 	}
1331 	f.field->set_brightness(left, right, top_left, top_right, bottom_left, bottom_right);
1332 }
1333 
1334 /*
1335 ===============
1336 Recalculate the caps for the given node.
1337  - Check terrain types for passability and flag buildability
1338 
1339 I hope this is understandable and maintainable.
1340 
1341 Note: due to inter-field dependencies, nodecaps calculations are split up
1342 into two passes. You should always perform both passes. See the comment
1343 above recalc_brightness.
1344 ===============
1345 */
recalc_nodecaps_pass1(const EditorGameBase & egbase,const FCoords & f)1346 void Map::recalc_nodecaps_pass1(const EditorGameBase& egbase, const FCoords& f) {
1347 	f.field->caps = calc_nodecaps_pass1(egbase, f, true);
1348 	f.field->max_caps = calc_nodecaps_pass1(egbase, f, false);
1349 }
1350 
1351 NodeCaps
calc_nodecaps_pass1(const EditorGameBase & egbase,const FCoords & f,bool consider_mobs) const1352 Map::calc_nodecaps_pass1(const EditorGameBase& egbase, const FCoords& f, bool consider_mobs) const {
1353 	uint8_t caps = CAPS_NONE;
1354 	const World& world = egbase.world();
1355 
1356 	// 1a) Get all the neighbours to make life easier
1357 	const FCoords tr = tr_n(f);
1358 	const FCoords tl = tl_n(f);
1359 	const FCoords l = l_n(f);
1360 
1361 	const TerrainDescription::Is tr_d_terrain_is =
1362 	   world.terrain_descr(tr.field->terrain_d()).get_is();
1363 	const TerrainDescription::Is tl_r_terrain_is =
1364 	   world.terrain_descr(tl.field->terrain_r()).get_is();
1365 	const TerrainDescription::Is tl_d_terrain_is =
1366 	   world.terrain_descr(tl.field->terrain_d()).get_is();
1367 	const TerrainDescription::Is l_r_terrain_is = world.terrain_descr(l.field->terrain_r()).get_is();
1368 	const TerrainDescription::Is f_d_terrain_is = world.terrain_descr(f.field->terrain_d()).get_is();
1369 	const TerrainDescription::Is f_r_terrain_is = world.terrain_descr(f.field->terrain_r()).get_is();
1370 
1371 	//  1b) Collect some information about the neighbours
1372 	uint8_t cnt_unwalkable = 0;
1373 	uint8_t cnt_water = 0;
1374 	uint8_t cnt_unreachable = 0;
1375 
1376 	if (tr_d_terrain_is & TerrainDescription::Is::kUnwalkable)
1377 		++cnt_unwalkable;
1378 	if (tl_r_terrain_is & TerrainDescription::Is::kUnwalkable)
1379 		++cnt_unwalkable;
1380 	if (tl_d_terrain_is & TerrainDescription::Is::kUnwalkable)
1381 		++cnt_unwalkable;
1382 	if (l_r_terrain_is & TerrainDescription::Is::kUnwalkable)
1383 		++cnt_unwalkable;
1384 	if (f_d_terrain_is & TerrainDescription::Is::kUnwalkable)
1385 		++cnt_unwalkable;
1386 	if (f_r_terrain_is & TerrainDescription::Is::kUnwalkable)
1387 		++cnt_unwalkable;
1388 
1389 	if (tr_d_terrain_is & TerrainDescription::Is::kWater)
1390 		++cnt_water;
1391 	if (tl_r_terrain_is & TerrainDescription::Is::kWater)
1392 		++cnt_water;
1393 	if (tl_d_terrain_is & TerrainDescription::Is::kWater)
1394 		++cnt_water;
1395 	if (l_r_terrain_is & TerrainDescription::Is::kWater)
1396 		++cnt_water;
1397 	if (f_d_terrain_is & TerrainDescription::Is::kWater)
1398 		++cnt_water;
1399 	if (f_r_terrain_is & TerrainDescription::Is::kWater)
1400 		++cnt_water;
1401 
1402 	if (tr_d_terrain_is & TerrainDescription::Is::kUnreachable)
1403 		++cnt_unreachable;
1404 	if (tl_r_terrain_is & TerrainDescription::Is::kUnreachable)
1405 		++cnt_unreachable;
1406 	if (tl_d_terrain_is & TerrainDescription::Is::kUnreachable)
1407 		++cnt_unreachable;
1408 	if (l_r_terrain_is & TerrainDescription::Is::kUnreachable)
1409 		++cnt_unreachable;
1410 	if (f_d_terrain_is & TerrainDescription::Is::kUnreachable)
1411 		++cnt_unreachable;
1412 	if (f_r_terrain_is & TerrainDescription::Is::kUnreachable)
1413 		++cnt_unreachable;
1414 
1415 	//  2) Passability
1416 
1417 	//  2a) If any of the neigbouring triangles is walkable this node is
1418 	//  walkable.
1419 	if (cnt_unwalkable < 6)
1420 		caps |= MOVECAPS_WALK;
1421 
1422 	//  2b) If all neighbouring triangles are water, the node is swimmable.
1423 	if (cnt_water == 6)
1424 		caps |= MOVECAPS_SWIM;
1425 
1426 	// 2c) [OVERRIDE] If any of the neighbouring triangles is really "bad" (such
1427 	// as lava), we can neither walk nor swim to this node.
1428 	if (cnt_unreachable)
1429 		caps &= ~(MOVECAPS_WALK | MOVECAPS_SWIM);
1430 
1431 	//  === everything below is used to check buildability ===
1432 
1433 	// if we are interested in the maximum theoretically available NodeCaps, this is not run
1434 	if (consider_mobs) {
1435 		//  3) General buildability check: if a "robust" MapObject is on this node
1436 		//  we cannot build anything on it. Exception: we can build flags on roads and waterways.
1437 		if (BaseImmovable* const imm = get_immovable(f))
1438 			if (!dynamic_cast<RoadBase const*>(imm) && imm->get_size() >= BaseImmovable::SMALL) {
1439 				// 3b) [OVERRIDE] check for "unwalkable" MapObjects
1440 				if (!imm->get_passable())
1441 					caps &= ~(MOVECAPS_WALK | MOVECAPS_SWIM);
1442 				return static_cast<NodeCaps>(caps);
1443 			}
1444 	}
1445 
1446 	//  4) Flags
1447 	//  We can build flags on anything that's walkable and buildable, with some
1448 	//  restrictions
1449 	if (caps & MOVECAPS_WALK) {
1450 		//  4b) Flags must be at least 2 edges apart
1451 		if (consider_mobs && find_immovables(egbase, Area<FCoords>(f, 1), nullptr,
1452 		                                     FindImmovableType(MapObjectType::FLAG)))
1453 			return static_cast<NodeCaps>(caps);
1454 		caps |= BUILDCAPS_FLAG;
1455 	}
1456 	return static_cast<NodeCaps>(caps);
1457 }
1458 
1459 /*
1460 ===============
1461 Second pass of nodecaps. Determine which kind of building (if any) can be built
1462 on this Field.
1463 
1464 Important: flag buildability has already been checked in the first pass.
1465 ===============
1466 */
recalc_nodecaps_pass2(const EditorGameBase & egbase,const FCoords & f)1467 void Map::recalc_nodecaps_pass2(const EditorGameBase& egbase, const FCoords& f) {
1468 	f.field->caps = calc_nodecaps_pass2(egbase, f, true);
1469 	f.field->max_caps =
1470 	   calc_nodecaps_pass2(egbase, f, false, static_cast<NodeCaps>(f.field->max_caps));
1471 }
1472 
calc_nodecaps_pass2(const EditorGameBase & egbase,const FCoords & f,bool consider_mobs,NodeCaps initcaps) const1473 NodeCaps Map::calc_nodecaps_pass2(const EditorGameBase& egbase,
1474                                   const FCoords& f,
1475                                   bool consider_mobs,
1476                                   NodeCaps initcaps) const {
1477 	uint8_t caps = consider_mobs ? f.field->caps : static_cast<uint8_t>(initcaps);
1478 
1479 	// NOTE  This dependency on the bottom-right neighbour is the reason
1480 	// NOTE  why the caps calculation is split into two passes
1481 	// NOTE  However this dependency has to be recalculated in case we are interested in the
1482 	// NOTE  maximum possible NodeCaps for this FCoord
1483 	const FCoords br = br_n(f);
1484 	if (consider_mobs) {
1485 		if (!(br.field->caps & BUILDCAPS_FLAG) &&
1486 		    (!br.field->get_immovable() ||
1487 		     br.field->get_immovable()->descr().type() != MapObjectType::FLAG))
1488 			return static_cast<NodeCaps>(caps);
1489 	} else {
1490 		if (!(calc_nodecaps_pass1(egbase, br, false) & BUILDCAPS_FLAG))
1491 			return static_cast<NodeCaps>(caps);
1492 	}
1493 
1494 	bool mine;
1495 	uint8_t buildsize = calc_buildsize(egbase, f, true, &mine, consider_mobs, initcaps);
1496 	if (buildsize < BaseImmovable::SMALL)
1497 		return static_cast<NodeCaps>(caps);
1498 	assert(buildsize >= BaseImmovable::SMALL && buildsize <= BaseImmovable::BIG);
1499 
1500 	if (buildsize == BaseImmovable::BIG) {
1501 		if (calc_buildsize(egbase, l_n(f), false, nullptr, consider_mobs, initcaps) <
1502 		       BaseImmovable::BIG ||
1503 		    calc_buildsize(egbase, tl_n(f), false, nullptr, consider_mobs, initcaps) <
1504 		       BaseImmovable::BIG ||
1505 		    calc_buildsize(egbase, tr_n(f), false, nullptr, consider_mobs, initcaps) <
1506 		       BaseImmovable::BIG)
1507 			buildsize = BaseImmovable::MEDIUM;
1508 	}
1509 
1510 	// Reduce building size if it would block connectivity
1511 	if (buildsize == BaseImmovable::BIG) {
1512 		static const WalkingDir cycledirs[10] = {
1513 		   WALK_NE, WALK_NE, WALK_NW, WALK_W, WALK_W, WALK_SW, WALK_SW, WALK_SE, WALK_E, WALK_E};
1514 		if (!is_cycle_connected(br, 10, cycledirs))
1515 			buildsize = BUILDCAPS_MEDIUM;
1516 	}
1517 	if (buildsize < BaseImmovable::BIG) {
1518 		static const WalkingDir cycledirs[6] = {WALK_NE, WALK_NW, WALK_W, WALK_SW, WALK_SE, WALK_E};
1519 		if (!is_cycle_connected(br, 6, cycledirs))
1520 			return static_cast<NodeCaps>(caps);
1521 	}
1522 
1523 	if (mine) {
1524 		if (static_cast<int32_t>(br.field->get_height()) - f.field->get_height() < 4)
1525 			caps |= BUILDCAPS_MINE;
1526 	} else {
1527 		Field::Height const f_height = f.field->get_height();
1528 
1529 		// Reduce building size based on slope of direct neighbours:
1530 		//  - slope >= 4: can't build anything here -> return
1531 		//  - slope >= 3: maximum size is small
1532 		{
1533 			MapFringeRegion<Area<FCoords>> mr(*this, Area<FCoords>(f, 1));
1534 			do {
1535 				uint16_t const slope = abs(mr.location().field->get_height() - f_height);
1536 				if (slope >= 4)
1537 					return static_cast<NodeCaps>(caps);
1538 				if (slope >= 3)
1539 					buildsize = BaseImmovable::SMALL;
1540 			} while (mr.advance(*this));
1541 		}
1542 		if (abs(br.field->get_height() - f_height) >= 2)
1543 			return static_cast<NodeCaps>(caps);
1544 
1545 		// Reduce building size based on height diff. of second order
1546 		// neighbours  If height difference between this field and second
1547 		// order neighbour is >= 3, we can only build a small house here.
1548 		// Additionally, we can potentially build a port on this field
1549 		// if one of the second order neighbours is swimmable.
1550 		if (buildsize >= BaseImmovable::MEDIUM) {
1551 			MapFringeRegion<Area<FCoords>> mr(*this, Area<FCoords>(f, 2));
1552 
1553 			do {
1554 				if (abs(mr.location().field->get_height() - f_height) >= 3) {
1555 					buildsize = BaseImmovable::SMALL;
1556 					break;
1557 				}
1558 			} while (mr.advance(*this));
1559 		}
1560 
1561 		if ((buildsize == BaseImmovable::BIG) && is_port_space(f) && !find_portdock(f).empty())
1562 			caps |= BUILDCAPS_PORT;
1563 
1564 		caps |= buildsize;
1565 	}
1566 	return static_cast<NodeCaps>(caps);
1567 }
1568 
1569 /**
1570  * Return the size of immovable that is supposed to be buildable on \p f,
1571  * based on immovables on \p f and its neighbours.
1572  * Sets \p ismine depending on whether the field is on mountaineous terrain
1573  * or not.
1574  * \p consider_mobs defines, whether mapobjects currently on \p f or neighbour fields should be
1575  * considered
1576  * for the calculation. If not (calculation of maximum theoretical possible buildsize) initcaps must
1577  * be set.
1578  */
calc_buildsize(const EditorGameBase & egbase,const FCoords & f,bool avoidnature,bool * ismine,bool consider_mobs,NodeCaps initcaps) const1579 int Map::calc_buildsize(const EditorGameBase& egbase,
1580                         const FCoords& f,
1581                         bool avoidnature,
1582                         bool* ismine,
1583                         bool consider_mobs,
1584                         NodeCaps initcaps) const {
1585 	if (consider_mobs) {
1586 		if (!(f.field->get_caps() & MOVECAPS_WALK))
1587 			return BaseImmovable::NONE;
1588 		if (BaseImmovable const* const immovable = get_immovable(f))
1589 			if (immovable->get_size() >= BaseImmovable::SMALL)
1590 				return BaseImmovable::NONE;
1591 	} else if (!(initcaps & MOVECAPS_WALK))
1592 		return BaseImmovable::NONE;
1593 
1594 	// Get all relevant neighbours and count terrain triangle types.
1595 	const FCoords tr = tr_n(f);
1596 	const FCoords tl = tl_n(f);
1597 	const FCoords l = l_n(f);
1598 
1599 	const World& world = egbase.world();
1600 	const TerrainDescription::Is terrains[6] = {world.terrain_descr(tr.field->terrain_d()).get_is(),
1601 	                                            world.terrain_descr(tl.field->terrain_r()).get_is(),
1602 	                                            world.terrain_descr(tl.field->terrain_d()).get_is(),
1603 	                                            world.terrain_descr(l.field->terrain_r()).get_is(),
1604 	                                            world.terrain_descr(f.field->terrain_d()).get_is(),
1605 	                                            world.terrain_descr(f.field->terrain_r()).get_is()};
1606 
1607 	uint32_t cnt_mineable = 0;
1608 	uint32_t cnt_walkable = 0;
1609 	for (uint32_t i = 0; i < 6; ++i) {
1610 		if (terrains[i] & TerrainDescription::Is::kWater ||
1611 		    terrains[i] & TerrainDescription::Is::kUnwalkable)
1612 			return BaseImmovable::NONE;
1613 		if (terrains[i] & TerrainDescription::Is::kMineable)
1614 			++cnt_mineable;
1615 		if (terrains[i] & TerrainDescription::Is::kWalkable)
1616 			++cnt_walkable;
1617 	}
1618 
1619 	if (cnt_mineable == 6) {
1620 		if (ismine)
1621 			*ismine = true;
1622 		return BaseImmovable::SMALL;
1623 	}
1624 	if (cnt_mineable || cnt_walkable)
1625 		return BaseImmovable::NONE;
1626 
1627 	// Adjust size based on neighbouring immovables
1628 	int buildsize = BaseImmovable::BIG;
1629 	if (consider_mobs) {
1630 		std::vector<ImmovableFound> objectlist;
1631 		find_immovables(egbase, Area<FCoords>(f, 1), &objectlist,
1632 		                FindImmovableSize(BaseImmovable::SMALL, BaseImmovable::BIG));
1633 		for (uint32_t i = 0; i < objectlist.size(); ++i) {
1634 			const BaseImmovable* obj = objectlist[i].object;
1635 			int objsize = obj->get_size();
1636 			if (objsize == BaseImmovable::NONE)
1637 				continue;
1638 			if (avoidnature && obj->descr().type() == MapObjectType::IMMOVABLE)
1639 				++objsize;
1640 			if (objsize + buildsize > BaseImmovable::BIG)
1641 				buildsize = BaseImmovable::BIG - objsize + 1;
1642 		}
1643 	}
1644 
1645 	if (ismine)
1646 		*ismine = false;
1647 	return buildsize;
1648 }
1649 
1650 /**
1651  * We call a cycle on the map simply connected
1652  * if the subgraph of the cycle which can be walked on is connected.
1653  *
1654  * The cycle is described as a \p start point plus
1655  * a description of the directions in which to walk from the starting point.
1656  * The array \p dirs must have length \p length, where \p length is
1657  * the length of the cycle.
1658  */
is_cycle_connected(const FCoords & start,uint32_t length,const WalkingDir * dirs) const1659 bool Map::is_cycle_connected(const FCoords& start, uint32_t length, const WalkingDir* dirs) const {
1660 	FCoords f = start;
1661 	bool prev_walkable = start.field->get_caps() & MOVECAPS_WALK;
1662 	uint32_t alternations = 0;
1663 
1664 	for (uint32_t i = 0; i < length; ++i) {
1665 		f = get_neighbour(f, dirs[i]);
1666 		const bool walkable = f.field->get_caps() & MOVECAPS_WALK;
1667 		alternations += walkable != prev_walkable;
1668 		if (alternations > 2)
1669 			return false;
1670 		prev_walkable = walkable;
1671 	}
1672 
1673 	assert(start == f);
1674 
1675 	return true;
1676 }
1677 
1678 /**
1679  * Returns a list of portdock fields (if any) that a port built at \p c should have.
1680  */
find_portdock(const Coords & c) const1681 std::vector<Coords> Map::find_portdock(const Coords& c) const {
1682 	static const WalkingDir cycledirs[16] = {WALK_NE, WALK_NE, WALK_NE, WALK_NW, WALK_NW, WALK_W,
1683 	                                         WALK_W,  WALK_W,  WALK_SW, WALK_SW, WALK_SW, WALK_SE,
1684 	                                         WALK_SE, WALK_E,  WALK_E,  WALK_E};
1685 	const FCoords start = br_n(br_n(get_fcoords(c)));
1686 	const Widelands::PlayerNumber owner = start.field->get_owned_by();
1687 	FCoords f = start;
1688 	std::vector<Coords> portdock;
1689 	for (uint32_t i = 0; i < 16; ++i) {
1690 		bool is_good_water = (f.field->get_caps() & (MOVECAPS_SWIM | MOVECAPS_WALK)) == MOVECAPS_SWIM;
1691 
1692 		// Any immovable here? (especially another portdock)
1693 		if (is_good_water && f.field->get_immovable()) {
1694 			is_good_water = false;
1695 		}
1696 
1697 		// If starting point is owned we make sure this field has the same owner
1698 		if (is_good_water && owner != neutral() && f.field->get_owned_by() != owner) {
1699 			is_good_water = false;
1700 		}
1701 
1702 		// ... and is not on a border
1703 		if (is_good_water && owner != neutral() && f.field->is_border()) {
1704 			is_good_water = false;
1705 		}
1706 
1707 		if (is_good_water) {
1708 			portdock.push_back(f);
1709 			// Occupy 2 fields maximum in order not to block space for other ports that
1710 			// might be built in the vicinity.
1711 			if (portdock.size() == 2) {
1712 				return portdock;
1713 			}
1714 		}
1715 
1716 		if (i < 15)
1717 			f = get_neighbour(f, cycledirs[i]);
1718 	}
1719 
1720 	return portdock;
1721 }
1722 
is_port_space_allowed(const EditorGameBase & egbase,const FCoords & fc) const1723 bool Map::is_port_space_allowed(const EditorGameBase& egbase, const FCoords& fc) const {
1724 	return (get_max_nodecaps(egbase, fc) & BUILDCAPS_SIZEMASK) == BUILDCAPS_BIG &&
1725 	       !find_portdock(fc).empty();
1726 }
1727 
1728 /// \returns true, if Coordinates are in port space list
is_port_space(const Coords & c) const1729 bool Map::is_port_space(const Coords& c) const {
1730 	return port_spaces_.count(c);
1731 }
1732 
set_port_space(const EditorGameBase & egbase,const Coords & c,bool set,bool force,bool recalculate_seafaring)1733 bool Map::set_port_space(const EditorGameBase& egbase,
1734                          const Coords& c,
1735                          bool set,
1736                          bool force,
1737                          bool recalculate_seafaring) {
1738 	bool success = false;
1739 	if (set) {
1740 		success = force || is_port_space_allowed(egbase, get_fcoords(c));
1741 		if (success) {
1742 			port_spaces_.insert(c);
1743 			recalculate_seafaring &= !allows_seafaring();
1744 		}
1745 	} else {
1746 		recalculate_seafaring &= allows_seafaring();
1747 		port_spaces_.erase(c);
1748 		success = true;
1749 	}
1750 	if (recalculate_seafaring) {
1751 		recalculate_allows_seafaring();
1752 	}
1753 	return success;
1754 }
1755 
1756 /**
1757  * Calculate the (Manhattan) distance from a to b
1758  * a and b are expected to be normalized!
1759  */
calc_distance(const Coords & a,const Coords & b) const1760 uint32_t Map::calc_distance(const Coords& a, const Coords& b) const {
1761 	uint32_t dist;
1762 	int32_t dy;
1763 
1764 	// do we fly up or down?
1765 	dy = b.y - a.y;
1766 	if (dy > static_cast<int32_t>(height_ >> 1))  //  wrap-around!
1767 		dy -= height_;
1768 	else if (dy < -static_cast<int32_t>(height_ >> 1))
1769 		dy += height_;
1770 
1771 	dist = abs(dy);
1772 
1773 	if (static_cast<int16_t>(dist) >= width_)
1774 		// no need to worry about x movement at all
1775 		return dist;
1776 
1777 	// [lx..rx] is the x-range we can cover simply by walking vertically
1778 	// towards b
1779 	// Hint: (~a.y & 1) is 1 for even rows, 0 for odd rows.
1780 	// This means we round UP for even rows, and we round DOWN for odd rows.
1781 	int32_t lx, rx;
1782 
1783 	lx = a.x - ((dist + (~a.y & 1)) >> 1);  // div 2
1784 	rx = lx + dist;
1785 
1786 	// Allow for wrap-around
1787 	// Yes, the second is an else if; see the above if (dist >= width_)
1788 	if (lx < 0)
1789 		lx += width_;
1790 	else if (rx >= static_cast<int32_t>(width_))
1791 		rx -= width_;
1792 
1793 	// Normal, non-wrapping case
1794 	if (lx <= rx) {
1795 		if (b.x < lx) {
1796 			int32_t dx1 = lx - b.x;
1797 			int32_t dx2 = b.x - (rx - width_);
1798 			dist += std::min(dx1, dx2);
1799 		} else if (b.x > rx) {
1800 			int32_t dx1 = b.x - rx;
1801 			int32_t dx2 = (lx + width_) - b.x;
1802 			dist += std::min(dx1, dx2);
1803 		}
1804 	} else {
1805 		// Reverse case
1806 		if (b.x > rx && b.x < lx) {
1807 			int32_t dx1 = b.x - rx;
1808 			int32_t dx2 = lx - b.x;
1809 			dist += std::min(dx1, dx2);
1810 		}
1811 	}
1812 
1813 	return dist;
1814 }
1815 
1816 #define BASE_COST_PER_FIELD 1800
1817 #define SLOPE_COST_DIVISOR 50
1818 #define SLOPE_COST_STEPS 8
1819 
1820 /*
1821 ===============
1822 Calculates the cost estimate between the two points.
1823 This function is used mainly for the path-finding estimate.
1824 ===============
1825 */
calc_cost_estimate(const Coords & a,const Coords & b) const1826 int32_t Map::calc_cost_estimate(const Coords& a, const Coords& b) const {
1827 	return calc_distance(a, b) * BASE_COST_PER_FIELD;
1828 }
1829 
1830 /**
1831  * \return a lower bound on the time required to walk from \p a to \p b
1832  */
calc_cost_lowerbound(const Coords & a,const Coords & b) const1833 int32_t Map::calc_cost_lowerbound(const Coords& a, const Coords& b) const {
1834 	return calc_distance(a, b) * calc_cost(-SLOPE_COST_STEPS);
1835 }
1836 
1837 /*
1838 ===============
1839 Calculate the hard cost of walking the given slope (positive means up,
1840 negative means down).
1841 The cost is in milliseconds it takes to walk.
1842 
1843 The time is calculated as BASE_COST_PER_FIELD * f, where
1844 
1845 f = 1.0 + d(Slope) - d(0)
1846 d = (Slope + SLOPE_COST_STEPS) * (Slope + SLOPE_COST_STEPS - 1)
1847        / (2 * SLOPE_COST_DIVISOR)
1848 
1849 Note that the actual calculations multiply through by (2 * SLOPE_COST_DIVISOR)
1850 to avoid using floating point numbers in game logic code.
1851 
1852 Slope is limited to the range [ -SLOPE_COST_STEPS; +oo [
1853 ===============
1854 */
1855 #define CALC_COST_D(slope) (((slope) + SLOPE_COST_STEPS) * ((slope) + SLOPE_COST_STEPS - 1))
1856 
calc_cost_d(int32_t slope)1857 static int32_t calc_cost_d(int32_t slope) {
1858 	if (slope < -SLOPE_COST_STEPS)
1859 		slope = -SLOPE_COST_STEPS;
1860 
1861 	return CALC_COST_D(slope);
1862 }
1863 
calc_cost(int32_t const slope) const1864 int32_t Map::calc_cost(int32_t const slope) const {
1865 	return BASE_COST_PER_FIELD * (2 * SLOPE_COST_DIVISOR + calc_cost_d(slope) - CALC_COST_D(0)) /
1866 	       (2 * SLOPE_COST_DIVISOR);
1867 }
1868 
1869 /*
1870 ===============
1871 Return the time it takes to walk the given step from coords in the given
1872 direction, in milliseconds.
1873 ===============
1874 */
calc_cost(const Coords & coords,const int32_t dir) const1875 int32_t Map::calc_cost(const Coords& coords, const int32_t dir) const {
1876 	FCoords f;
1877 	int32_t startheight;
1878 	int32_t delta;
1879 
1880 	// Calculate the height delta
1881 	f = get_fcoords(coords);
1882 	startheight = f.field->get_height();
1883 
1884 	get_neighbour(f, dir, &f);
1885 	delta = f.field->get_height() - startheight;
1886 
1887 	return calc_cost(delta);
1888 }
1889 
1890 /*
1891 ===============
1892 Calculate the average cost of walking the given step in both directions.
1893 ===============
1894 */
calc_bidi_cost(const Coords & coords,const int32_t dir) const1895 int32_t Map::calc_bidi_cost(const Coords& coords, const int32_t dir) const {
1896 	FCoords f;
1897 	int32_t startheight;
1898 	int32_t delta;
1899 
1900 	// Calculate the height delta
1901 	f = get_fcoords(coords);
1902 	startheight = f.field->get_height();
1903 
1904 	get_neighbour(f, dir, &f);
1905 	delta = f.field->get_height() - startheight;
1906 
1907 	return (calc_cost(delta) + calc_cost(-delta)) / 2;
1908 }
1909 
1910 /*
1911 ===============
1912 Calculate the cost of walking the given path.
1913 If either of the forward or backward pointers is set, it will be filled in
1914 with the cost of walking in said direction.
1915 ===============
1916 */
calc_cost(const Path & path,int32_t * const forward,int32_t * const backward) const1917 void Map::calc_cost(const Path& path, int32_t* const forward, int32_t* const backward) const {
1918 	Coords coords = path.get_start();
1919 
1920 	if (forward)
1921 		*forward = 0;
1922 	if (backward)
1923 		*backward = 0;
1924 
1925 	const Path::StepVector::size_type nr_steps = path.get_nsteps();
1926 	for (Path::StepVector::size_type i = 0; i < nr_steps; ++i) {
1927 		const Direction dir = path[i];
1928 
1929 		if (forward)
1930 			*forward += calc_cost(coords, dir);
1931 		get_neighbour(coords, dir, &coords);
1932 		if (backward)
1933 			*backward += calc_cost(coords, get_reverse_dir(dir));
1934 	}
1935 }
1936 
1937 /// Get a node's neighbour by direction.
get_neighbour(const Coords & f,Direction const dir,Coords * const o) const1938 void Map::get_neighbour(const Coords& f, Direction const dir, Coords* const o) const {
1939 	switch (dir) {
1940 	case WALK_NW:
1941 		get_tln(f, o);
1942 		break;
1943 	case WALK_NE:
1944 		get_trn(f, o);
1945 		break;
1946 	case WALK_E:
1947 		get_rn(f, o);
1948 		break;
1949 	case WALK_SE:
1950 		get_brn(f, o);
1951 		break;
1952 	case WALK_SW:
1953 		get_bln(f, o);
1954 		break;
1955 	case WALK_W:
1956 		get_ln(f, o);
1957 		break;
1958 	default:
1959 		NEVER_HERE();
1960 	}
1961 }
1962 
get_neighbour(const FCoords & f,Direction const dir,FCoords * const o) const1963 void Map::get_neighbour(const FCoords& f, Direction const dir, FCoords* const o) const {
1964 	switch (dir) {
1965 	case WALK_NW:
1966 		get_tln(f, o);
1967 		break;
1968 	case WALK_NE:
1969 		get_trn(f, o);
1970 		break;
1971 	case WALK_E:
1972 		get_rn(f, o);
1973 		break;
1974 	case WALK_SE:
1975 		get_brn(f, o);
1976 		break;
1977 	case WALK_SW:
1978 		get_bln(f, o);
1979 		break;
1980 	case WALK_W:
1981 		get_ln(f, o);
1982 		break;
1983 	default:
1984 		NEVER_HERE();
1985 	}
1986 }
1987 
get_correct_loader(const std::string & filename)1988 std::unique_ptr<MapLoader> Map::get_correct_loader(const std::string& filename) {
1989 	std::unique_ptr<MapLoader> result;
1990 
1991 	std::string lower_filename = filename;
1992 	boost::algorithm::to_lower(lower_filename);
1993 
1994 	try {
1995 		if (boost::algorithm::ends_with(lower_filename, kWidelandsMapExtension)) {
1996 			result.reset(new WidelandsMapLoader(g_fs->make_sub_file_system(filename), this));
1997 		} else if (boost::algorithm::ends_with(lower_filename, kS2MapExtension1) ||
1998 		           boost::algorithm::ends_with(lower_filename, kS2MapExtension2)) {
1999 			result.reset(new S2MapLoader(filename, *this));
2000 		}
2001 	} catch (const FileError& e) {
2002 		// file might not have existed
2003 		log("Map::get_correct_loader: File error: %s\n", e.what());
2004 	} catch (std::exception& e) {
2005 		log("Map::get_correct_loader: Unknown error: %s\n", e.what());
2006 	}
2007 	return result;
2008 }
2009 
2010 /**
2011  * Finds a path from start to end for a MapObject with the given movecaps.
2012  *
2013  * The path is stored in \p path, as a series of MapObject::WalkingDir entries.
2014  *
2015  * \param persist tells the function how hard it should try to find a path:
2016  * If \p persist is \c 0, the function will never give up early. Otherwise, the
2017  * function gives up when it becomes clear that the path takes longer than
2018  * persist * bird's distance of flat terrain.
2019  * Note that if the terrain contains steep hills, the cost calculation will
2020  * cause the search to terminate earlier than you may think. If persist==1,
2021  * findpath() can only find a path if the terrain is completely flat.
2022  *
2023  * \param checkstep findpath() calls this checkstep functor-like to determine
2024  * whether moving from one field to another is legal.
2025  *
2026  * \param instart starting point of the search
2027  * \param inend end point of the search
2028  * \param path will receive the found path if successful
2029  * \param flags UNDOCUMENTED
2030  * \param type whether to find a path for a ware or a worker
2031  *
2032  * \return the cost of the path (in milliseconds of normal walking
2033  * speed) or -1 if no path has been found.
2034  */
2035 // TODO(unknown): Document parameters instart, inend, path, flags
findpath(Coords instart,Coords inend,int32_t const persist,Path & path,const CheckStep & checkstep,uint32_t const flags,uint32_t const caps_sensitivity,WareWorker type) const2036 int32_t Map::findpath(Coords instart,
2037                       Coords inend,
2038                       int32_t const persist,
2039                       Path& path,
2040                       const CheckStep& checkstep,
2041                       uint32_t const flags,
2042                       uint32_t const caps_sensitivity,
2043                       WareWorker type) const {
2044 	FCoords start;
2045 	FCoords end;
2046 	int32_t upper_cost_limit;
2047 	FCoords cur;
2048 
2049 	normalize_coords(instart);
2050 	normalize_coords(inend);
2051 
2052 	start = FCoords(instart, &operator[](instart));
2053 	end = FCoords(inend, &operator[](inend));
2054 
2055 	path.path_.clear();
2056 
2057 	// Some stupid cases...
2058 	if (start == end) {
2059 		path.start_ = start;
2060 		path.end_ = end;
2061 		return 0;  // duh...
2062 	}
2063 
2064 	if (!checkstep.reachable_dest(*this, end)) {
2065 		return -1;
2066 	}
2067 
2068 	if (!persist)
2069 		upper_cost_limit = 0;
2070 	else
2071 		// assume flat terrain
2072 		upper_cost_limit = persist * calc_cost_estimate(start, end);
2073 
2074 	// Actual pathfinding
2075 	std::shared_ptr<Pathfields> pathfields = pathfieldmgr_->allocate();
2076 	Pathfield::Queue Open(type);
2077 	Pathfield* curpf = &pathfields->fields[start.field - fields_.get()];
2078 	curpf->cycle = pathfields->cycle;
2079 	curpf->real_cost = 0;
2080 	curpf->estim_cost = calc_cost_lowerbound(start, end);
2081 	curpf->backlink = IDLE;
2082 
2083 	Open.push(curpf);
2084 
2085 	for (;;) {
2086 		if (Open.empty())  // there simply is no path
2087 			return -1;
2088 		curpf = Open.top();
2089 		Open.pop(curpf);
2090 
2091 		cur.field = fields_.get() + (curpf - pathfields->fields.get());
2092 		get_coords(*cur.field, cur);
2093 
2094 		if (upper_cost_limit && curpf->real_cost > upper_cost_limit)
2095 			break;  // upper cost limit reached, give up
2096 		if (cur == end)
2097 			break;  // found our target
2098 
2099 		// avoid bias by using different orders when pathfinding
2100 		static const int8_t order1[] = {WALK_NW, WALK_NE, WALK_E, WALK_SE, WALK_SW, WALK_W};
2101 		static const int8_t order2[] = {WALK_NW, WALK_W, WALK_SW, WALK_SE, WALK_E, WALK_NE};
2102 		int8_t const* direction = ((cur.x + cur.y) & 1) ? order1 : order2;
2103 
2104 		// Check all the 6 neighbours
2105 		for (uint32_t i = 6; i; i--, direction++) {
2106 			FCoords neighb;
2107 			int32_t cost;
2108 
2109 			get_neighbour(cur, *direction, &neighb);
2110 			Pathfield& neighbpf = pathfields->fields[neighb.field - fields_.get()];
2111 
2112 			// Is the field Closed already?
2113 			if (neighbpf.cycle == pathfields->cycle && !neighbpf.heap_cookie.is_active())
2114 				continue;
2115 
2116 			// Check passability
2117 			if (!checkstep.allowed(
2118 			       *this, cur, neighb, *direction, neighb == end ? CheckStep::stepLast : cur == start ?
2119 			                                                       CheckStep::stepFirst :
2120 			                                                       CheckStep::stepNormal))
2121 				continue;
2122 
2123 			// Calculate cost
2124 			cost = curpf->real_cost + ((flags & fpBidiCost) ? calc_bidi_cost(cur, *direction) :
2125 			                                                  calc_cost(cur, *direction));
2126 
2127 			// If required (indicated by caps_sensitivity) we increase the path costs
2128 			// if the path is just crossing a field with building capabilities
2129 			if (caps_sensitivity > 0) {
2130 				int32_t buildcaps_score = neighb.field->get_caps() & BUILDCAPS_SIZEMASK;
2131 				buildcaps_score += (neighb.field->get_caps() & BUILDCAPS_MINE) ? 1 : 0;
2132 				buildcaps_score += (neighb.field->get_caps() & BUILDCAPS_PORT) ? 9 : 0;
2133 				cost += buildcaps_score * caps_sensitivity;
2134 			}
2135 
2136 			if (neighbpf.cycle != pathfields->cycle) {
2137 				// add to open list
2138 				neighbpf.cycle = pathfields->cycle;
2139 				neighbpf.real_cost = cost;
2140 				neighbpf.estim_cost = calc_cost_lowerbound(neighb, end);
2141 				neighbpf.backlink = *direction;
2142 				Open.push(&neighbpf);
2143 			} else if (neighbpf.cost(type) > cost + neighbpf.estim_cost) {
2144 				// found a better path to a field that's already Open
2145 				neighbpf.real_cost = cost;
2146 				neighbpf.backlink = *direction;
2147 				Open.decrease_key(&neighbpf);
2148 			}
2149 		}
2150 	}
2151 
2152 	// Now unwind the taken route (even if we couldn't find a complete one!)
2153 	int32_t const result = cur == end ? curpf->real_cost : -1;
2154 
2155 	path.start_ = start;
2156 	path.end_ = cur;
2157 
2158 	path.path_.clear();
2159 
2160 	while (curpf->backlink != IDLE) {
2161 		path.path_.push_back(curpf->backlink);
2162 
2163 		// Reverse logic! (WALK_NW needs to find the SE neighbour)
2164 		get_neighbour(cur, get_reverse_dir(curpf->backlink), &cur);
2165 		curpf = &pathfields->fields[cur.field - fields_.get()];
2166 	}
2167 
2168 	return result;
2169 }
2170 
can_reach_by_water(const Coords & field) const2171 bool Map::can_reach_by_water(const Coords& field) const {
2172 	FCoords fc = get_fcoords(field);
2173 
2174 	if (fc.field->nodecaps() & MOVECAPS_SWIM)
2175 		return true;
2176 	if (!(fc.field->nodecaps() & MOVECAPS_WALK))
2177 		return false;
2178 
2179 	FCoords neighb;
2180 
2181 	for (Direction dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) {
2182 		if (get_neighbour(fc, dir).field->nodecaps() & MOVECAPS_SWIM)
2183 			return true;
2184 	}
2185 
2186 	return false;
2187 }
2188 
change_terrain(const EditorGameBase & egbase,TCoords<FCoords> const c,DescriptionIndex const terrain)2189 int32_t Map::change_terrain(const EditorGameBase& egbase,
2190                             TCoords<FCoords> const c,
2191                             DescriptionIndex const terrain) {
2192 	c.node.field->set_terrain(c.t, terrain);
2193 
2194 	// remove invalid resources if necessary
2195 	// check vertex to which the triangle belongs
2196 	if (!is_resource_valid(egbase.world(), c.node, c.node.field->get_resources())) {
2197 		clear_resources(c.node);
2198 	}
2199 
2200 	// always check south-east vertex
2201 	Widelands::FCoords f_se(c.node);
2202 	get_neighbour(f_se, Widelands::WALK_SE, &f_se);
2203 	if (!is_resource_valid(egbase.world(), f_se, f_se.field->get_resources())) {
2204 		clear_resources(f_se);
2205 	}
2206 
2207 	// check south-west vertex if d-Triangle is changed, check east vertex if r-Triangle is changed
2208 	Widelands::FCoords f_sw_e(c.node);
2209 	get_neighbour(f_sw_e, c.t == TriangleIndex::D ? Widelands::WALK_SW : Widelands::WALK_E, &f_sw_e);
2210 	if (!is_resource_valid(egbase.world(), f_sw_e, f_sw_e.field->get_resources())) {
2211 		clear_resources(f_sw_e);
2212 	}
2213 
2214 	Notifications::publish(
2215 	   NoteFieldTerrainChanged{c.node, static_cast<MapIndex>(c.node.field - &fields_[0])});
2216 
2217 	// Changing the terrain can affect ports, which can be up to 3 fields away.
2218 	constexpr int kPotentiallyAffectedNeighbors = 3;
2219 	recalc_for_field_area(egbase, Area<FCoords>(c.node, kPotentiallyAffectedNeighbors));
2220 	return kPotentiallyAffectedNeighbors;
2221 }
2222 
is_resource_valid(const Widelands::World & world,const Widelands::FCoords & c,DescriptionIndex curres) const2223 bool Map::is_resource_valid(const Widelands::World& world,
2224                             const Widelands::FCoords& c,
2225                             DescriptionIndex curres) const {
2226 	if (curres == Widelands::kNoResource)
2227 		return true;
2228 
2229 	Widelands::FCoords f1;
2230 
2231 	int32_t count = 0;
2232 
2233 	//  this field
2234 	count += world.terrain_descr(c.field->terrain_r()).is_resource_valid(curres);
2235 	count += world.terrain_descr(c.field->terrain_d()).is_resource_valid(curres);
2236 
2237 	//  If one of the neighbours is impassable, count its resource stronger.
2238 	//  top left neigbour
2239 	get_neighbour(c, Widelands::WALK_NW, &f1);
2240 	count += world.terrain_descr(f1.field->terrain_r()).is_resource_valid(curres);
2241 	count += world.terrain_descr(f1.field->terrain_d()).is_resource_valid(curres);
2242 
2243 	//  top right neigbour
2244 	get_neighbour(c, Widelands::WALK_NE, &f1);
2245 	count += world.terrain_descr(f1.field->terrain_d()).is_resource_valid(curres);
2246 
2247 	//  left neighbour
2248 	get_neighbour(c, Widelands::WALK_W, &f1);
2249 	count += world.terrain_descr(f1.field->terrain_r()).is_resource_valid(curres);
2250 
2251 	return count > 1;
2252 }
2253 
ensure_resource_consistency(const World & world)2254 void Map::ensure_resource_consistency(const World& world) {
2255 	for (MapIndex i = 0; i < max_index(); ++i) {
2256 		auto fcords = get_fcoords(fields_[i]);
2257 		if (!is_resource_valid(world, fcords, fcords.field->get_resources())) {
2258 			clear_resources(fcords);
2259 		}
2260 	}
2261 }
2262 
initialize_resources(const FCoords & c,const DescriptionIndex resource_type,ResourceAmount amount)2263 void Map::initialize_resources(const FCoords& c,
2264                                const DescriptionIndex resource_type,
2265                                ResourceAmount amount) {
2266 	// You cannot have an amount of nothing.
2267 	if (resource_type == Widelands::kNoResource) {
2268 		amount = 0;
2269 	}
2270 	c.field->resources = resource_type;
2271 	c.field->initial_res_amount = amount;
2272 	c.field->res_amount = amount;
2273 }
2274 
set_resources(const FCoords & c,ResourceAmount amount)2275 void Map::set_resources(const FCoords& c, ResourceAmount amount) {
2276 	// You cannot change the amount of resources on a field without resources.
2277 	if (c.field->resources == Widelands::kNoResource) {
2278 		return;
2279 	}
2280 	c.field->res_amount = amount;
2281 }
2282 
clear_resources(const FCoords & c)2283 void Map::clear_resources(const FCoords& c) {
2284 	initialize_resources(c, Widelands::kNoResource, 0);
2285 }
2286 
set_height(const EditorGameBase & egbase,const FCoords fc,uint8_t const new_value)2287 uint32_t Map::set_height(const EditorGameBase& egbase, const FCoords fc, uint8_t const new_value) {
2288 	assert(new_value <= MAX_FIELD_HEIGHT);
2289 	assert(fields_.get() <= fc.field);
2290 	assert(fc.field < fields_.get() + max_index());
2291 	fc.field->height = new_value;
2292 	uint32_t radius = 2;
2293 	check_neighbour_heights(fc, radius);
2294 	recalc_for_field_area(egbase, Area<FCoords>(fc, radius));
2295 	return radius;
2296 }
2297 
2298 uint32_t
change_height(const EditorGameBase & egbase,Area<FCoords> area,int16_t const difference)2299 Map::change_height(const EditorGameBase& egbase, Area<FCoords> area, int16_t const difference) {
2300 	{
2301 		MapRegion<Area<FCoords>> mr(*this, area);
2302 		do {
2303 			if (difference < 0 && mr.location().field->height < static_cast<uint8_t>(-difference))
2304 				mr.location().field->height = 0;
2305 			else if (static_cast<int16_t>(MAX_FIELD_HEIGHT) - difference <
2306 			         static_cast<int16_t>(mr.location().field->height))
2307 				mr.location().field->height = MAX_FIELD_HEIGHT;
2308 			else
2309 				mr.location().field->height += difference;
2310 		} while (mr.advance(*this));
2311 	}
2312 	uint32_t regional_radius = 0;
2313 	MapFringeRegion<Area<FCoords>> mr(*this, area);
2314 	do {
2315 		uint32_t local_radius = 0;
2316 		check_neighbour_heights(mr.location(), local_radius);
2317 		regional_radius = std::max(regional_radius, local_radius);
2318 	} while (mr.advance(*this));
2319 	area.radius += regional_radius + 2;
2320 	recalc_for_field_area(egbase, area);
2321 	return area.radius;
2322 }
2323 
2324 uint32_t
set_height(const EditorGameBase & egbase,Area<FCoords> area,HeightInterval height_interval)2325 Map::set_height(const EditorGameBase& egbase, Area<FCoords> area, HeightInterval height_interval) {
2326 	assert(height_interval.valid());
2327 	assert(height_interval.max <= MAX_FIELD_HEIGHT);
2328 	{
2329 		MapRegion<Area<FCoords>> mr(*this, area);
2330 		do {
2331 			if (mr.location().field->height < height_interval.min)
2332 				mr.location().field->height = height_interval.min;
2333 			else if (height_interval.max < mr.location().field->height)
2334 				mr.location().field->height = height_interval.max;
2335 		} while (mr.advance(*this));
2336 	}
2337 	++area.radius;
2338 	{
2339 		MapFringeRegion<Area<FCoords>> mr(*this, area);
2340 		bool changed;
2341 		do {
2342 			changed = false;
2343 			height_interval.min = height_interval.min < MAX_FIELD_HEIGHT_DIFF ?
2344 			                         0 :
2345 			                         height_interval.min - MAX_FIELD_HEIGHT_DIFF;
2346 			height_interval.max = height_interval.max < MAX_FIELD_HEIGHT - MAX_FIELD_HEIGHT_DIFF ?
2347 			                         height_interval.max + MAX_FIELD_HEIGHT_DIFF :
2348 			                         MAX_FIELD_HEIGHT;
2349 			do {
2350 				if (mr.location().field->height < height_interval.min) {
2351 					mr.location().field->height = height_interval.min;
2352 					changed = true;
2353 				} else if (height_interval.max < mr.location().field->height) {
2354 					mr.location().field->height = height_interval.max;
2355 					changed = true;
2356 				}
2357 			} while (mr.advance(*this));
2358 			mr.extend(*this);
2359 		} while (changed);
2360 		area.radius = mr.radius();
2361 	}
2362 	recalc_for_field_area(egbase, area);
2363 	return area.radius;
2364 }
2365 
2366 /*
2367 ===========
2368 Map::check_neighbour_heights()
2369 
2370 This private functions checks all neighbours of a field
2371 if they are in valid boundaries, if not, they are reheighted
2372 accordingly.
2373 The radius of modified fields is stored in area.
2374 =============
2375 */
check_neighbour_heights(FCoords coords,uint32_t & area)2376 void Map::check_neighbour_heights(FCoords coords, uint32_t& area) {
2377 	assert(fields_.get() <= coords.field);
2378 	assert(coords.field < fields_.get() + max_index());
2379 
2380 	int32_t height = coords.field->get_height();
2381 	bool check[] = {false, false, false, false, false, false};
2382 
2383 	const FCoords n[] = {
2384 	   tl_n(coords), tr_n(coords), l_n(coords), r_n(coords), bl_n(coords), br_n(coords)};
2385 
2386 	for (uint8_t i = 0; i < 6; ++i) {
2387 		Field& f = *n[i].field;
2388 		const int32_t diff = height - f.get_height();
2389 		if (diff > MAX_FIELD_HEIGHT_DIFF) {
2390 			++area;
2391 			f.set_height(height - MAX_FIELD_HEIGHT_DIFF);
2392 			check[i] = true;
2393 		}
2394 		if (diff < -MAX_FIELD_HEIGHT_DIFF) {
2395 			++area;
2396 			f.set_height(height + MAX_FIELD_HEIGHT_DIFF);
2397 			check[i] = true;
2398 		}
2399 	}
2400 
2401 	for (uint8_t i = 0; i < 6; ++i)
2402 		if (check[i])
2403 			check_neighbour_heights(n[i], area);
2404 }
2405 
allows_seafaring() const2406 bool Map::allows_seafaring() const {
2407 	return allows_seafaring_;
2408 }
2409 
2410 // This check can become very expensive, so we only recalculate this on relevant map changes.
recalculate_allows_seafaring()2411 void Map::recalculate_allows_seafaring() {
2412 
2413 	// There need to be at least 2 port spaces for seafaring to make sense
2414 	if (get_port_spaces().size() < 2) {
2415 		allows_seafaring_ = false;
2416 		Notifications::publish(NoteMapOptions());
2417 		return;
2418 	}
2419 
2420 	std::set<Coords> reachable_from_previous_ports;
2421 
2422 	for (const Coords& c : get_port_spaces()) {
2423 		std::queue<Coords> positions_to_check;
2424 		std::set<Coords> reachable_from_current_port;
2425 		FCoords fc = get_fcoords(c);
2426 
2427 		// Get portdock slots for this port
2428 		for (const Coords& portdock : find_portdock(fc)) {
2429 			reachable_from_current_port.insert(portdock);
2430 			positions_to_check.push(portdock);
2431 		}
2432 
2433 		// Pick up all positions that can be reached from the current port
2434 		while (!positions_to_check.empty()) {
2435 			// Take a copy, because we'll pop it
2436 			const Coords current_position = positions_to_check.front();
2437 			positions_to_check.pop();
2438 
2439 			// Found one
2440 			if (reachable_from_previous_ports.count(current_position) > 0) {
2441 				allows_seafaring_ = true;
2442 				Notifications::publish(NoteMapOptions());
2443 				return;
2444 			}
2445 
2446 			// Adding the neighbors to the list
2447 			for (uint8_t i = 1; i <= 6; ++i) {
2448 				FCoords neighbour;
2449 				get_neighbour(get_fcoords(current_position), i, &neighbour);
2450 				if ((neighbour.field->get_caps() & (MOVECAPS_SWIM | MOVECAPS_WALK)) == MOVECAPS_SWIM) {
2451 					if (reachable_from_current_port.count(neighbour) == 0) {
2452 						reachable_from_current_port.insert(neighbour);
2453 						positions_to_check.push(neighbour);
2454 					}
2455 				}
2456 			}
2457 		}
2458 
2459 		// Couldn't connect to another port, so we add our reachable nodes to the list
2460 		for (const Coords& reachable_coord : reachable_from_current_port) {
2461 			reachable_from_previous_ports.insert(reachable_coord);
2462 		}
2463 	}
2464 	allows_seafaring_ = false;
2465 	Notifications::publish(NoteMapOptions());
2466 }
2467 
cleanup_port_spaces(const EditorGameBase & egbase)2468 void Map::cleanup_port_spaces(const EditorGameBase& egbase) {
2469 	// Temporary set to avoid problems with concurrent container operations
2470 	PortSpacesSet clean_me_up;
2471 	for (const Coords& c : get_port_spaces()) {
2472 		if (!is_port_space_allowed(egbase, get_fcoords(c))) {
2473 			clean_me_up.insert(c);
2474 		}
2475 	}
2476 	for (const Coords& c : clean_me_up) {
2477 		set_port_space(egbase, c, false);
2478 	}
2479 	recalculate_allows_seafaring();
2480 }
2481 
has_artifacts()2482 bool Map::has_artifacts() {
2483 	for (MapIndex i = 0; i < max_index(); ++i) {
2484 		if (upcast(Immovable, immovable, fields_[i].get_immovable())) {
2485 			if (immovable->descr().has_attribute(immovable->descr().get_attribute_id("artifact"))) {
2486 				return true;
2487 			}
2488 		}
2489 	}
2490 	return false;
2491 }
2492 
2493 #define MAX_RADIUS 32
calc_influence(Coords const a,Area<> const area) const2494 MilitaryInfluence Map::calc_influence(Coords const a, Area<> const area) const {
2495 	const int16_t w = get_width();
2496 	const int16_t h = get_height();
2497 	MilitaryInfluence influence =
2498 	   std::max(std::min(std::min(abs(a.x - area.x), abs(a.x - area.x + w)), abs(a.x - area.x - w)),
2499 	            std::min(std::min(abs(a.y - area.y), abs(a.y - area.y + h)), abs(a.y - area.y - h)));
2500 
2501 	influence = influence > area.radius ? 0 : influence == 0 ? MAX_RADIUS : MAX_RADIUS - influence;
2502 	influence *= influence;
2503 
2504 	return influence;
2505 }
2506 
to_set(Area<Coords> area) const2507 std::set<Coords> Map::to_set(Area<Coords> area) const {
2508 	std::set<Coords> result;
2509 	MapRegion<Area<Coords>> mr(*this, area);
2510 	do {
2511 		result.insert(mr.location());
2512 	} while (mr.advance(*this));
2513 	return result;
2514 }
2515 
2516 // Returns all triangles whose corners are all in the given area
triangles_in_region(std::set<Coords> area) const2517 std::set<TCoords<Coords>> Map::triangles_in_region(std::set<Coords> area) const {
2518 	std::set<TCoords<Coords>> result;
2519 	for (const Coords& c : area) {
2520 		if (!area.count(br_n(c))) {
2521 			continue;
2522 		}
2523 		if (area.count(r_n(c))) {
2524 			result.insert(TCoords<Coords>(c, TriangleIndex::R));
2525 		}
2526 		if (area.count(bl_n(c))) {
2527 			result.insert(TCoords<Coords>(c, TriangleIndex::D));
2528 		}
2529 	}
2530 	return result;
2531 }
2532 
2533 }  // namespace Widelands
2534