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