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 #ifndef WL_LOGIC_MAP_H
21 #define WL_LOGIC_MAP_H
22
23 #include <memory>
24
25 #include "base/i18n.h"
26 #include "economy/itransport_cost_calculator.h"
27 #include "logic/field.h"
28 #include "logic/map_objects/findimmovable.h"
29 #include "logic/map_objects/tribes/wareworker.h"
30 #include "logic/map_objects/walkingdir.h"
31 #include "logic/map_revision.h"
32 #include "logic/objective.h"
33 #include "logic/widelands.h"
34 #include "logic/widelands_geometry.h"
35 #include "notifications/note_ids.h"
36 #include "notifications/notifications.h"
37
38 class FileSystem;
39 struct S2MapLoader;
40
41 namespace Widelands {
42
43 class MapLoader;
44 struct MapGenerator;
45 struct PathfieldManager;
46 class World;
47
48 // Global list of available map dimensions.
49 const std::vector<int32_t> kMapDimensions = {64, 80, 96, 112, 128, 144, 160, 176, 192, 208,
50 224, 240, 256, 272, 288, 304, 320, 336, 352, 368,
51 384, 400, 416, 432, 448, 464, 480, 496, 512};
52
53 struct Path;
54 class Immovable;
55
56 struct NoteFieldTerrainChanged {
57 CAN_BE_SENT_AS_NOTE(NoteId::FieldTerrainChanged)
58
59 FCoords fc;
60 MapIndex map_index;
61 };
62
63 struct ImmovableFound {
64 BaseImmovable* object;
65 Coords coords;
66 };
67
68 /*
69 FindImmovable
70 FindBob
71 FindNode
72 FindResource
73 CheckStep
74
75 Predicates used in path finding and find functions.
76 */
77
78 struct FindBob {
79 // Return true if this bob should be returned by find_bobs.
80 virtual bool accept(Bob*) const = 0;
~FindBobFindBob81 virtual ~FindBob() {
82 } // make gcc shut up
83 };
84 struct FindNode;
85 struct CheckStep;
86
87 /*
88 Some very simple default predicates (more predicates below Map).
89 */
90 struct FindBobAlwaysTrue : public FindBob {
acceptFindBobAlwaysTrue91 bool accept(Bob*) const override {
92 return true;
93 }
~FindBobAlwaysTrueFindBobAlwaysTrue94 ~FindBobAlwaysTrue() override {
95 } // make gcc shut up
96 };
97
98 // Helper struct to save certain elemental data of a field without an actual instance of Field
99 struct FieldData {
100 FieldData(const Field& f);
101
102 std::string immovable;
103 std::list<std::string> bobs;
104 uint8_t height;
105 DescriptionIndex resources;
106 uint8_t resource_amount;
107 Field::Terrains terrains;
108 };
109 // used for undoing map resize
110 struct ResizeHistory {
ResizeHistoryResizeHistory111 ResizeHistory() : size(0, 0) {
112 }
113
114 Extent size;
115 std::list<FieldData> fields;
116 std::set<Coords> port_spaces;
117 std::vector<Coords> starting_positions;
118 };
119
120 /** class Map
121 *
122 * This really identifies a map like it is in the game
123 *
124 * Odd rows are shifted FIELD_WIDTH/2 to the right. This means that moving
125 * up and down depends on the row numbers:
126 * even odd
127 * top-left -1/-1 0/-1
128 * top-right 0/-1 +1/-1
129 * bottom-left -1/+1 0/+1
130 * bottom-right 0/+1 +1/+1
131 *
132 * Warning: width and height must be even
133 */
134 class Map : public ITransportCostCalculator {
135 public:
136 friend class EditorGameBase;
137 friend class MapLoader;
138 friend class MapVersionPacket;
139 friend struct ::S2MapLoader;
140 friend struct MainMenuNewMap;
141 friend struct MapAStarBase;
142 friend struct MapGenerator;
143 friend struct MapElementalPacket;
144 friend struct WidelandsMapLoader;
145
146 using PortSpacesSet = std::set<Coords>;
147 using Objectives = std::map<std::string, std::unique_ptr<Objective>>;
148
149 enum { // flags for findpath()
150
151 // use bidirection cost instead of normal cost calculations
152 // should be used for road building
153 fpBidiCost = 1,
154 };
155
156 // ORed bits for scenario types
157 using ScenarioTypes = size_t;
158 enum { NO_SCENARIO = 0, SP_SCENARIO = 1, MP_SCENARIO = 2 };
159
160 Map();
161 ~Map() override;
162
163 /// Returns the correct initialized loader for the given mapfile
164 std::unique_ptr<MapLoader> get_correct_loader(const std::string& filename);
165
166 void cleanup();
167
168 void create_empty_map // for editor
169 (const EditorGameBase&,
170 uint32_t w = 64,
171 uint32_t h = 64,
172 const Widelands::DescriptionIndex default_terrain = 0,
173 const std::string& name = _("No Name"),
174 const std::string& author = pgettext("author_name", "Unknown"),
175 const std::string& description = _("No description defined"));
176
177 void recalc_whole_map(const EditorGameBase&);
178 void recalc_for_field_area(const EditorGameBase&, Area<FCoords>);
179
180 /**
181 * If the valuable fields are empty, calculates all fields that could be conquered by a player
182 * throughout a game. Useful for territorial win conditions. Returns the amount of valuable
183 * fields.
184 */
185 size_t count_all_conquerable_fields();
186
187 /**
188 * If the valuable fields are empty, calculates which fields do not have the given caps and adds
189 * the to the list of valuable fields. Useful for win conditions. Returns the amount of valuable
190 * fields.
191 */
192 size_t count_all_fields_excluding_caps(NodeCaps caps);
193
194 /**
195 * Counts the valuable fields that are owned by each player. Only players that currently own a
196 * field are added. Returns a map of <player number, number of owned fields>.
197 */
198 std::map<PlayerNumber, size_t>
199 count_owned_valuable_fields(const std::string& immovable_attribute) const;
200
201 /***
202 * Ensures that resources match their adjacent terrains.
203 */
204 void ensure_resource_consistency(const World&);
205
206 /***
207 * Recalculates all default resources.
208 *
209 * This is just needed for the game, not for
210 * the editor. Since there, default resources
211 * are not shown.
212 */
213 void recalc_default_resources(const World&);
214
215 void set_nrplayers(PlayerNumber);
216
217 void set_starting_pos(PlayerNumber, const Coords&);
get_starting_pos(PlayerNumber const p)218 Coords get_starting_pos(PlayerNumber const p) const {
219 assert(1 <= p && p <= get_nrplayers());
220 return starting_pos_[p - 1];
221 }
222
223 void set_filename(const std::string& filename);
224 void set_author(const std::string& author);
225 void set_name(const std::string& name);
226 void set_description(const std::string& description);
227 void set_hint(const std::string& hint);
228 void set_background(const std::string& image_path);
229 void add_tag(const std::string& tag);
230 void delete_tag(const std::string& tag);
set_scenario_types(ScenarioTypes t)231 void set_scenario_types(ScenarioTypes t) {
232 scenario_types_ = t;
233 }
234
235 // Allows access to the filesystem of the map to access auxiliary files.
236 // This can be nullptr if this file is new.
237 FileSystem* filesystem() const;
238 // swap the filesystem after load / save
239 void swap_filesystem(std::unique_ptr<FileSystem>& fs);
240 void reset_filesystem();
241
242 // informational functions
get_filename()243 const std::string& get_filename() const {
244 return filename_;
245 }
get_author()246 const std::string& get_author() const {
247 return author_;
248 }
get_name()249 const std::string& get_name() const {
250 return name_;
251 }
get_description()252 const std::string& get_description() const {
253 return description_;
254 }
get_hint()255 const std::string& get_hint() const {
256 return hint_;
257 }
get_background()258 const std::string& get_background() const {
259 return background_;
260 }
261
262 using Tags = std::set<std::string>;
get_tags()263 const Tags& get_tags() const {
264 return tags_;
265 }
clear_tags()266 void clear_tags() {
267 tags_.clear();
268 }
has_tag(const std::string & s)269 bool has_tag(const std::string& s) const {
270 return tags_.count(s);
271 }
272
get_suggested_teams()273 const std::vector<SuggestedTeamLineup>& get_suggested_teams() const {
274 return suggested_teams_;
275 }
276
get_nrplayers()277 PlayerNumber get_nrplayers() const {
278 return nrplayers_;
279 }
scenario_types()280 ScenarioTypes scenario_types() const {
281 return scenario_types_;
282 }
extent()283 Extent extent() const {
284 return Extent(width_, height_);
285 }
get_width()286 int16_t get_width() const {
287 return width_;
288 }
get_height()289 int16_t get_height() const {
290 return height_;
291 }
292
293 // Map compatibility information for the website
294 int needs_widelands_version_after() const;
295
296 // The next few functions are only valid when the map is loaded as a
297 // scenario.
298 const std::string& get_scenario_player_tribe(PlayerNumber) const;
299 const std::string& get_scenario_player_name(PlayerNumber) const;
300 const std::string& get_scenario_player_ai(PlayerNumber) const;
301 bool get_scenario_player_closeable(PlayerNumber) const;
302 void set_scenario_player_tribe(PlayerNumber, const std::string&);
303 void set_scenario_player_name(PlayerNumber, const std::string&);
304 void set_scenario_player_ai(PlayerNumber, const std::string&);
305 void set_scenario_player_closeable(PlayerNumber, bool);
306
307 /// \returns the maximum theoretical possible nodecaps (no blocking bobs, immovables etc.)
308 NodeCaps get_max_nodecaps(const EditorGameBase&, const FCoords&) const;
309
310 BaseImmovable* get_immovable(const Coords&) const;
311 uint32_t find_bobs(const EditorGameBase&,
312 const Area<FCoords>,
313 std::vector<Bob*>* list,
314 const FindBob& functor = FindBobAlwaysTrue()) const;
315 uint32_t find_reachable_bobs(const EditorGameBase&,
316 const Area<FCoords>,
317 std::vector<Bob*>* list,
318 const CheckStep&,
319 const FindBob& functor = FindBobAlwaysTrue()) const;
320 uint32_t find_immovables(const EditorGameBase&,
321 const Area<FCoords>,
322 std::vector<ImmovableFound>* list,
323 const FindImmovable& = find_immovable_always_true()) const;
324 uint32_t find_reachable_immovables(const EditorGameBase&,
325 const Area<FCoords>,
326 std::vector<ImmovableFound>* list,
327 const CheckStep&,
328 const FindImmovable& = find_immovable_always_true()) const;
329 uint32_t
330 find_reachable_immovables_unique(const EditorGameBase&,
331 const Area<FCoords>,
332 std::vector<BaseImmovable*>& list,
333 const CheckStep&,
334 const FindImmovable& = find_immovable_always_true()) const;
335 uint32_t find_fields(const EditorGameBase&,
336 const Area<FCoords>,
337 std::vector<Coords>* list,
338 const FindNode& functor) const;
339 uint32_t find_reachable_fields(const EditorGameBase&,
340 const Area<FCoords>,
341 std::vector<Coords>* list,
342 const CheckStep&,
343 const FindNode&) const;
344
345 // Field logic
346 static MapIndex get_index(const Coords&, int16_t width);
347 MapIndex get_index(const Coords&) const;
max_index()348 MapIndex max_index() const {
349 return width_ * height_;
350 }
351 Field& operator[](MapIndex) const;
352 Field& operator[](const Coords&) const;
353 FCoords get_fcoords(const Coords&) const;
354 static void normalize_coords(Coords&, int16_t, int16_t);
355 void normalize_coords(Coords&) const;
356 FCoords get_fcoords(Field&) const;
357 void get_coords(Field& f, Coords& c) const;
358
359 uint32_t calc_distance(const Coords&, const Coords&) const;
360
361 int32_t calc_cost_estimate(const Coords&, const Coords&) const override;
362 int32_t calc_cost_lowerbound(const Coords&, const Coords&) const;
363 int32_t calc_cost(int32_t slope) const;
364 int32_t calc_cost(const Coords&, int32_t dir) const;
365 int32_t calc_bidi_cost(const Coords&, int32_t dir) const;
366 void calc_cost(const Path&, int32_t* forward, int32_t* backward) const;
367
368 void get_ln(const Coords&, Coords*) const;
369 void get_ln(const FCoords&, FCoords*) const;
370 Coords l_n(const Coords&) const;
371 FCoords l_n(const FCoords&) const;
372 void get_rn(const Coords&, Coords*) const;
373 void get_rn(const FCoords&, FCoords*) const;
374 Coords r_n(const Coords&) const;
375 FCoords r_n(const FCoords&) const;
376 void get_tln(const Coords&, Coords*) const;
377 void get_tln(const FCoords&, FCoords*) const;
378 Coords tl_n(const Coords&) const;
379 FCoords tl_n(const FCoords&) const;
380 void get_trn(const Coords&, Coords*) const;
381 void get_trn(const FCoords&, FCoords*) const;
382 Coords tr_n(const Coords&) const;
383 FCoords tr_n(const FCoords&) const;
384 void get_bln(const Coords&, Coords*) const;
385 void get_bln(const FCoords&, FCoords*) const;
386 Coords bl_n(const Coords&) const;
387 FCoords bl_n(const FCoords&) const;
388 void get_brn(const Coords&, Coords*) const;
389 void get_brn(const FCoords&, FCoords*) const;
390 Coords br_n(const Coords&) const;
391 FCoords br_n(const FCoords&) const;
392
393 void get_neighbour(const Coords&, Direction dir, Coords*) const;
394 void get_neighbour(const FCoords&, Direction dir, FCoords*) const;
395 FCoords get_neighbour(const FCoords&, Direction dir) const;
396
397 std::set<Coords> to_set(Area<Coords> area) const;
398 std::set<TCoords<Coords>> triangles_in_region(std::set<Coords> area) const;
399
400 // Pathfinding
401 int32_t findpath(Coords instart,
402 Coords inend,
403 const int32_t persist,
404 Path&,
405 const CheckStep&,
406 const uint32_t flags = 0,
407 const uint32_t caps_sensitivity = 0,
408 WareWorker type = wwWORKER) const;
409
410 /**
411 * We can reach a field by water either if it has MOVECAPS_SWIM or if it has
412 * MOVECAPS_WALK and at least one of the neighbours has MOVECAPS_SWIM
413 */
414 bool can_reach_by_water(const Coords&) const;
415
416 /// Sets the height to a value. Recalculates brightness. Changes the
417 /// surrounding nodes if necessary. Returns the radius that covers all
418 /// changes that were made.
419 ///
420 /// Do not call this to set the height of each node in an area to the same
421 /// value, because it adjusts the heights of surrounding nodes in each call,
422 /// so it will be terribly slow. Use set_height for Area for that purpose
423 /// instead.
424 uint32_t set_height(const EditorGameBase&, FCoords, Field::Height);
425
426 /// Changes the height of the nodes in an Area by a difference.
427 uint32_t change_height(const EditorGameBase&, Area<FCoords>, int16_t difference);
428
429 /// Initializes the 'initial_resources' on 'coords' to the 'resource_type'
430 /// with the given 'amount'.
431 void initialize_resources(const FCoords& coords,
432 DescriptionIndex resource_type,
433 ResourceAmount amount);
434
435 /// Sets the number of resources of the field to 'amount'. The type of the
436 /// resource on this field is not changed.
437 void set_resources(const FCoords& coords, ResourceAmount amount);
438
439 /// Clears the resources, i.e. the amount will be set to 0 and the type of
440 /// resources will be kNoResource.
441 void clear_resources(const FCoords& coords);
442
443 /**
444 * Ensures that the height of each node within radius from fc is in
445 * height_interval. If the height is < height_interval.min, it is changed to
446 * height_interval.min. If the height is > height_interval.max, it is changed
447 * to height_interval.max. Otherwise it is left unchanged.
448 *
449 * Recalculates brightness. Changes the surrounding nodes if necessary.
450 * Returns the radius of the area that covers all changes that were made.
451 *
452 * Calling this is much faster than calling change_height for each node in
453 * the area, because this adjusts the surrounding nodes only once, after all
454 * nodes in the area had their new height set.
455 */
456 uint32_t set_height(const EditorGameBase&, Area<FCoords>, HeightInterval height_interval);
457
458 /***
459 * Changes the given triangle's terrain. This happens in the editor and might
460 * happen in the game too if some kind of land increasement is implemented (like
461 * drying swamps). The nodecaps need to be recalculated. If terrain was changed from
462 * or to water, we need to recalculate_allows_seafaring too, depending on the situation.
463 *
464 * @return the radius of changes.
465 */
466 int32_t change_terrain(const EditorGameBase&, TCoords<FCoords>, DescriptionIndex);
467
468 /***
469 * Verify if a resource attached to a vertex has enough adjacent matching terrains to be valid.
470 *
471 * To qualify as valid, resources need to be surrounded by at least two matching terrains.
472 */
473 bool is_resource_valid(const Widelands::World&,
474 const Widelands::FCoords& c,
475 DescriptionIndex curres) const;
476
477 // The objectives that are defined in this map if it is a scenario.
objectives()478 const Objectives& objectives() const {
479 return objectives_;
480 }
mutable_objectives()481 Objectives* mutable_objectives() {
482 return &objectives_;
483 }
484
mutable_valuable_fields()485 std::set<FCoords>* mutable_valuable_fields() {
486 return &valuable_fields_;
487 }
488
489 /// Returns the military influence on a location from an area.
490 MilitaryInfluence calc_influence(Coords, Area<>) const;
491
492 /// Translate the whole map so that the given point becomes the new origin.
493 void set_origin(const Coords&);
494
495 // Port space specific functions
496
497 /// Checks whether the maximum theoretical possible NodeCap of the field is big,
498 /// and there is room for a port space
499 bool is_port_space_allowed(const EditorGameBase&, const FCoords& fc) const;
500 bool is_port_space(const Coords& c) const;
501
502 /// If 'set', set the space at 'c' as port space, otherwise unset.
503 /// 'force' sets the port space even if it isn't viable, and is to be used for map loading only.
504 /// Returns whether the port space was set/unset successfully.
505 bool set_port_space(const EditorGameBase&,
506 const Widelands::Coords& c,
507 bool set,
508 bool force = false,
509 bool recalculate_seafaring = false);
get_port_spaces()510 const PortSpacesSet& get_port_spaces() const {
511 return port_spaces_;
512 }
513 std::vector<Coords> find_portdock(const Widelands::Coords& c) const;
514
515 /// Return true if there are at least 2 port spaces that can be reached from each other by water
516 bool allows_seafaring() const;
517 /// Calculate whether there are at least 2 port spaces that can be reached from each other by
518 /// water and set the allows_seafaring property
519 void recalculate_allows_seafaring();
520 /// Remove all port spaces that are not valid (Buildcap < big or not enough space for a
521 /// portdock).
522 void cleanup_port_spaces(const EditorGameBase&);
523
524 /// Checks whether there are any artifacts on the map
525 bool has_artifacts();
526
527 // Visible for testing.
528 void set_size(uint32_t w, uint32_t h);
529
530 // Change the map size. Must not be used outside the editor.
531 void resize(EditorGameBase&, Coords, int32_t w, int32_t h);
532 // Used only to undo a resize() operation.
533 // Force-resets the entire map's state to the given preserved state.
534 void set_to(EditorGameBase&, ResizeHistory);
535 // Creates a ResizeHistory that can be passed to set_to() later.
536 // This has to save the entire map's state because resize operations
537 // may affect all fields when resolving height differences etc.
538 ResizeHistory dump_state(const EditorGameBase&) const;
539
540 uint32_t get_waterway_max_length() const;
541 void set_waterway_max_length(uint32_t max_length);
542
543 protected:
544 /// Calculate map compatibility information for the website if it wasn't defined in the map
545 /// packet. If is_post_one_world is true, this map wasn't created for a specific world (Widelands
546 /// versions up to Build 18).
547 void calculate_needs_widelands_version_after(bool is_post_one_world);
548
549 private:
550 void recalc_border(const FCoords&);
551 void recalc_brightness(const FCoords&);
552 void recalc_nodecaps_pass1(const EditorGameBase&, const FCoords&);
553 void recalc_nodecaps_pass2(const EditorGameBase&, const FCoords& f);
554 NodeCaps
555 calc_nodecaps_pass1(const EditorGameBase&, const FCoords&, bool consider_mobs = true) const;
556 NodeCaps calc_nodecaps_pass2(const EditorGameBase&,
557 const FCoords&,
558 bool consider_mobs = true,
559 NodeCaps initcaps = CAPS_NONE) const;
560 void check_neighbour_heights(FCoords, uint32_t& radius);
561 int calc_buildsize(const EditorGameBase&,
562 const FCoords& f,
563 bool avoidnature,
564 bool* ismine = nullptr,
565 bool consider_mobs = true,
566 NodeCaps initcaps = CAPS_NONE) const;
567 bool is_cycle_connected(const FCoords& start, uint32_t length, const WalkingDir* dirs) const;
568 template <typename functorT>
569 void
570 find_reachable(const EditorGameBase&, const Area<FCoords>&, const CheckStep&, functorT&) const;
571 template <typename functorT>
572 void find(const EditorGameBase&, const Area<FCoords>&, functorT&) const;
573
574 /// # of players this map supports (!= Game's number of players!)
575 PlayerNumber nrplayers_;
576 ScenarioTypes scenario_types_; // whether the map is playable as scenario
577
578 int16_t width_;
579 int16_t height_;
580 std::string filename_;
581 std::string author_;
582 std::string name_;
583 std::string description_;
584 std::string hint_;
585 std::string background_;
586 Tags tags_;
587 std::vector<SuggestedTeamLineup> suggested_teams_;
588
589 std::vector<Coords> starting_pos_; // players' starting positions
590
591 std::unique_ptr<Field[]> fields_;
592
593 std::unique_ptr<PathfieldManager> pathfieldmgr_;
594 std::vector<std::string> scenario_tribes_;
595 std::vector<std::string> scenario_names_;
596 std::vector<std::string> scenario_ais_;
597 std::vector<bool> scenario_closeables_;
598
599 // The map file as a filesystem.
600 std::unique_ptr<FileSystem> filesystem_;
601
602 PortSpacesSet port_spaces_;
603 bool allows_seafaring_;
604
605 uint32_t waterway_max_length_;
606
607 Objectives objectives_;
608
609 // Fields that are important for the player to own in a win condition
610 std::set<FCoords> valuable_fields_;
611
612 MapVersion map_version_;
613 };
614
615 /*
616 ==============================================================================
617
618 Field arithmetics
619
620 ==============================================================================
621 */
622
get_index(const Coords & c,int16_t const width)623 inline MapIndex Map::get_index(const Coords& c, int16_t const width) {
624 assert(0 < width);
625 assert(0 <= c.x);
626 assert(c.x < width);
627 assert(0 <= c.y);
628 return c.y * width + c.x;
629 }
630
get_index(const Coords & c)631 inline MapIndex Map::get_index(const Coords& c) const {
632 return get_index(c, width_);
633 }
634
635 inline Field& Map::operator[](MapIndex const i) const {
636 return fields_[i];
637 }
638 inline Field& Map::operator[](const Coords& c) const {
639 return operator[](get_index(c, width_));
640 }
641
get_fcoords(const Coords & c)642 inline FCoords Map::get_fcoords(const Coords& c) const {
643 return FCoords(c, &operator[](c));
644 }
645
normalize_coords(Coords & c)646 inline void Map::normalize_coords(Coords& c) const {
647 normalize_coords(c, width_, height_);
648 }
649
normalize_coords(Coords & c,int16_t w,int16_t h)650 inline void Map::normalize_coords(Coords& c, int16_t w, int16_t h) {
651 while (c.x < 0) {
652 c.x += w;
653 }
654 while (c.x >= w) {
655 c.x -= w;
656 }
657 while (c.y < 0) {
658 c.y += h;
659 }
660 while (c.y >= h) {
661 c.y -= h;
662 }
663 }
664
665 /**
666 * Calculate the field coordates from the pointer
667 */
get_fcoords(Field & f)668 inline FCoords Map::get_fcoords(Field& f) const {
669 const int32_t i = &f - fields_.get();
670 return FCoords(Coords(i % width_, i / width_), &f);
671 }
get_coords(Field & f,Coords & c)672 inline void Map::get_coords(Field& f, Coords& c) const {
673 c = get_fcoords(f);
674 }
675
676 /** get_ln, get_rn, get_tln, get_trn, get_bln, get_brn
677 *
678 * Calculate the coordinates and Field pointer of a neighboring field.
679 * Assume input coordinates are valid.
680 *
681 * Note: Input coordinates are passed as value because we have to allow
682 * usage get_XXn(foo, &foo).
683 */
get_ln(const Coords & f,Coords * const o)684 inline void Map::get_ln(const Coords& f, Coords* const o) const {
685 assert(0 <= f.x);
686 assert(f.x < width_);
687 assert(0 <= f.y);
688 assert(f.y < height_);
689 o->y = f.y;
690 o->x = (f.x ? f.x : width_) - 1;
691 assert(0 <= o->x);
692 assert(0 <= o->y);
693 assert(o->x < width_);
694 assert(o->y < height_);
695 }
696
get_ln(const FCoords & f,FCoords * const o)697 inline void Map::get_ln(const FCoords& f, FCoords* const o) const {
698 assert(0 <= f.x);
699 assert(f.x < width_);
700 assert(0 <= f.y);
701 assert(f.y < height_);
702 assert(fields_.get() <= f.field);
703 assert(f.field < fields_.get() + max_index());
704 o->y = f.y;
705 o->x = f.x - 1;
706 o->field = f.field - 1;
707 if (o->x == -1) {
708 o->x = width_ - 1;
709 o->field += width_;
710 }
711 assert(0 <= o->x);
712 assert(o->x < width_);
713 assert(0 <= o->y);
714 assert(o->y < height_);
715 assert(fields_.get() <= o->field);
716 assert(o->field < fields_.get() + max_index());
717 }
l_n(const Coords & f)718 inline Coords Map::l_n(const Coords& f) const {
719 assert(0 <= f.x);
720 assert(f.x < width_);
721 assert(0 <= f.y);
722 assert(f.y < height_);
723 Coords result(f.x - 1, f.y);
724 if (result.x == -1)
725 result.x = width_ - 1;
726 assert(0 <= result.x);
727 assert(result.x < width_);
728 assert(0 <= result.y);
729 assert(result.y < height_);
730 return result;
731 }
l_n(const FCoords & f)732 inline FCoords Map::l_n(const FCoords& f) const {
733 assert(0 <= f.x);
734 assert(f.x < width_);
735 assert(0 <= f.y);
736 assert(f.y < height_);
737 assert(fields_.get() <= f.field);
738 assert(f.field < fields_.get() + max_index());
739 FCoords result(Coords(f.x - 1, f.y), f.field - 1);
740 if (result.x == -1) {
741 result.x = width_ - 1;
742 result.field += width_;
743 }
744 assert(0 <= result.x);
745 assert(result.x < width_);
746 assert(0 <= result.y);
747 assert(result.y < height_);
748 assert(fields_.get() <= result.field);
749 assert(result.field < fields_.get() + max_index());
750 return result;
751 }
752
get_rn(const Coords & f,Coords * const o)753 inline void Map::get_rn(const Coords& f, Coords* const o) const {
754 assert(0 <= f.x);
755 assert(f.x < width_);
756 assert(0 <= f.y);
757 assert(f.y < height_);
758 o->y = f.y;
759 o->x = f.x + 1;
760 if (o->x == width_)
761 o->x = 0;
762 assert(0 <= o->x);
763 assert(o->x < width_);
764 assert(0 <= o->y);
765 assert(o->y < height_);
766 }
767
get_rn(const FCoords & f,FCoords * const o)768 inline void Map::get_rn(const FCoords& f, FCoords* const o) const {
769 assert(0 <= f.x);
770 assert(f.x < width_);
771 assert(0 <= f.y);
772 assert(f.y < height_);
773 assert(fields_.get() <= f.field);
774 assert(f.field < fields_.get() + max_index());
775 o->y = f.y;
776 o->x = f.x + 1;
777 o->field = f.field + 1;
778 if (o->x == width_) {
779 o->x = 0;
780 o->field -= width_;
781 }
782 assert(0 <= o->x);
783 assert(o->x < width_);
784 assert(0 <= o->y);
785 assert(o->y < height_);
786 assert(fields_.get() <= o->field);
787 assert(o->field < fields_.get() + max_index());
788 }
r_n(const Coords & f)789 inline Coords Map::r_n(const Coords& f) const {
790 assert(0 <= f.x);
791 assert(f.x < width_);
792 assert(0 <= f.y);
793 assert(f.y < height_);
794 Coords result(f.x + 1, f.y);
795 if (result.x == width_)
796 result.x = 0;
797 assert(0 <= result.x);
798 assert(result.x < width_);
799 assert(0 <= result.y);
800 assert(result.y < height_);
801 return result;
802 }
r_n(const FCoords & f)803 inline FCoords Map::r_n(const FCoords& f) const {
804 assert(0 <= f.x);
805 assert(f.x < width_);
806 assert(0 <= f.y);
807 assert(f.y < height_);
808 assert(fields_.get() <= f.field);
809 assert(f.field < fields_.get() + max_index());
810 FCoords result(Coords(f.x + 1, f.y), f.field + 1);
811 if (result.x == width_) {
812 result.x = 0;
813 result.field -= width_;
814 }
815 assert(0 <= result.x);
816 assert(result.x < width_);
817 assert(0 <= result.y);
818 assert(result.y < height_);
819 assert(fields_.get() <= result.field);
820 assert(result.field < fields_.get() + max_index());
821 return result;
822 }
823
824 // top-left: even: -1/-1 odd: 0/-1
get_tln(const Coords & f,Coords * const o)825 inline void Map::get_tln(const Coords& f, Coords* const o) const {
826 assert(0 <= f.x);
827 assert(f.x < width_);
828 assert(0 <= f.y);
829 assert(f.y < height_);
830 o->y = f.y - 1;
831 o->x = f.x;
832 if (o->y & 1) {
833 if (o->y == -1)
834 o->y = height_ - 1;
835 o->x = (o->x ? o->x : width_) - 1;
836 }
837 assert(0 <= o->x);
838 assert(o->x < width_);
839 assert(0 <= o->y);
840 assert(o->y < height_);
841 }
842
get_tln(const FCoords & f,FCoords * const o)843 inline void Map::get_tln(const FCoords& f, FCoords* const o) const {
844 assert(0 <= f.x);
845 assert(f.x < width_);
846 assert(0 <= f.y);
847 assert(f.y < height_);
848 assert(fields_.get() <= f.field);
849 assert(f.field < fields_.get() + max_index());
850 o->y = f.y - 1;
851 o->x = f.x;
852 o->field = f.field - width_;
853 if (o->y & 1) {
854 if (o->y == -1) {
855 o->y = height_ - 1;
856 o->field += max_index();
857 }
858 --o->x;
859 --o->field;
860 if (o->x == -1) {
861 o->x = width_ - 1;
862 o->field += width_;
863 }
864 }
865 assert(0 <= o->x);
866 assert(o->x < width_);
867 assert(0 <= o->y);
868 assert(o->y < height_);
869 assert(fields_.get() <= o->field);
870 assert(o->field < fields_.get() + max_index());
871 }
tl_n(const Coords & f)872 inline Coords Map::tl_n(const Coords& f) const {
873 assert(0 <= f.x);
874 assert(f.x < width_);
875 assert(0 <= f.y);
876 assert(f.y < height_);
877 Coords result(f.x, f.y - 1);
878 if (result.y & 1) {
879 if (result.y == -1)
880 result.y = height_ - 1;
881 --result.x;
882 if (result.x == -1)
883 result.x = width_ - 1;
884 }
885 assert(0 <= result.x);
886 assert(result.x < width_);
887 assert(0 <= result.y);
888 assert(result.y < height_);
889 return result;
890 }
tl_n(const FCoords & f)891 inline FCoords Map::tl_n(const FCoords& f) const {
892 assert(0 <= f.x);
893 assert(f.x < width_);
894 assert(0 <= f.y);
895 assert(f.y < height_);
896 assert(fields_.get() <= f.field);
897 assert(f.field < fields_.get() + max_index());
898 FCoords result(Coords(f.x, f.y - 1), f.field - width_);
899 if (result.y & 1) {
900 if (result.y == -1) {
901 result.y = height_ - 1;
902 result.field += max_index();
903 }
904 --result.x;
905 --result.field;
906 if (result.x == -1) {
907 result.x = width_ - 1;
908 result.field += width_;
909 }
910 }
911 assert(0 <= result.x);
912 assert(result.x < width_);
913 assert(0 <= result.y);
914 assert(result.y < height_);
915 assert(fields_.get() <= result.field);
916 assert(result.field < fields_.get() + max_index());
917 return result;
918 }
919
920 // top-right: even: 0/-1 odd: +1/-1
get_trn(const Coords & f,Coords * const o)921 inline void Map::get_trn(const Coords& f, Coords* const o) const {
922 assert(0 <= f.x);
923 assert(f.x < width_);
924 assert(0 <= f.y);
925 assert(f.y < height_);
926 o->x = f.x;
927 if (f.y & 1) {
928 ++o->x;
929 if (o->x == width_)
930 o->x = 0;
931 }
932 o->y = (f.y ? f.y : height_) - 1;
933 assert(0 <= o->x);
934 assert(o->x < width_);
935 assert(0 <= o->y);
936 assert(o->y < height_);
937 }
938
get_trn(const FCoords & f,FCoords * const o)939 inline void Map::get_trn(const FCoords& f, FCoords* const o) const {
940 assert(0 <= f.x);
941 assert(f.x < width_);
942 assert(0 <= f.y);
943 assert(f.y < height_);
944 assert(fields_.get() <= f.field);
945 assert(f.field < fields_.get() + max_index());
946 o->x = f.x;
947 o->field = f.field - width_;
948 if (f.y & 1) {
949 ++o->x;
950 ++o->field;
951 if (o->x == width_) {
952 o->x = 0;
953 o->field -= width_;
954 }
955 }
956 o->y = f.y - 1;
957 if (o->y == -1) {
958 o->y = height_ - 1;
959 o->field += max_index();
960 }
961 assert(0 <= o->x);
962 assert(o->x < width_);
963 assert(0 <= o->y);
964 assert(o->y < height_);
965 assert(fields_.get() <= o->field);
966 assert(o->field < fields_.get() + max_index());
967 }
tr_n(const Coords & f)968 inline Coords Map::tr_n(const Coords& f) const {
969 assert(0 <= f.x);
970 assert(f.x < width_);
971 assert(0 <= f.y);
972 assert(f.y < height_);
973 Coords result(f.x, f.y - 1);
974 if (f.y & 1) {
975 ++result.x;
976 if (result.x == width_)
977 result.x = 0;
978 }
979 if (result.y == -1)
980 result.y = height_ - 1;
981 assert(0 <= result.x);
982 assert(result.x < width_);
983 assert(0 <= result.y);
984 assert(result.y < height_);
985 return result;
986 }
tr_n(const FCoords & f)987 inline FCoords Map::tr_n(const FCoords& f) const {
988 assert(0 <= f.x);
989 assert(f.x < width_);
990 assert(0 <= f.y);
991 assert(f.y < height_);
992 assert(fields_.get() <= f.field);
993 assert(f.field < fields_.get() + max_index());
994 FCoords result(Coords(f.x, f.y - 1), f.field - width_);
995 if (f.y & 1) {
996 ++result.x;
997 ++result.field;
998 if (result.x == width_) {
999 result.x = 0;
1000 result.field -= width_;
1001 }
1002 }
1003 if (result.y == -1) {
1004 result.y = height_ - 1;
1005 result.field += max_index();
1006 }
1007 assert(0 <= result.x);
1008 assert(result.x < width_);
1009 assert(0 <= result.y);
1010 assert(result.y < height_);
1011 assert(fields_.get() <= result.field);
1012 assert(result.field < fields_.get() + max_index());
1013 return result;
1014 }
1015
1016 // bottom-left: even: -1/+1 odd: 0/+1
get_bln(const Coords & f,Coords * const o)1017 inline void Map::get_bln(const Coords& f, Coords* const o) const {
1018 assert(0 <= f.x);
1019 assert(f.x < width_);
1020 assert(0 <= f.y);
1021 assert(f.y < height_);
1022 o->y = f.y + 1;
1023 o->x = f.x;
1024 if (o->y == height_)
1025 o->y = 0;
1026 if (o->y & 1)
1027 o->x = (o->x ? o->x : width_) - 1;
1028 assert(0 <= o->x);
1029 assert(o->x < width_);
1030 assert(0 <= o->y);
1031 assert(o->y < height_);
1032 }
1033
get_bln(const FCoords & f,FCoords * const o)1034 inline void Map::get_bln(const FCoords& f, FCoords* const o) const {
1035 assert(0 <= f.x);
1036 assert(f.x < width_);
1037 assert(0 <= f.y);
1038 assert(f.y < height_);
1039 assert(fields_.get() <= f.field);
1040 assert(f.field < fields_.get() + max_index());
1041 o->y = f.y + 1;
1042 o->x = f.x;
1043 o->field = f.field + width_;
1044 if (o->y == height_) {
1045 o->y = 0;
1046 o->field -= max_index();
1047 }
1048 if (o->y & 1) {
1049 --o->x;
1050 --o->field;
1051 if (o->x == -1) {
1052 o->x = width_ - 1;
1053 o->field += width_;
1054 }
1055 }
1056 assert(0 <= o->x);
1057 assert(o->x < width_);
1058 assert(0 <= o->y);
1059 assert(o->y < height_);
1060 assert(fields_.get() <= o->field);
1061 assert(o->field < fields_.get() + max_index());
1062 }
bl_n(const Coords & f)1063 inline Coords Map::bl_n(const Coords& f) const {
1064 assert(0 <= f.x);
1065 assert(f.x < width_);
1066 assert(0 <= f.y);
1067 assert(f.y < height_);
1068 Coords result(f.x, f.y + 1);
1069 if (result.y == height_)
1070 result.y = 0;
1071 if (result.y & 1) {
1072 --result.x;
1073 if (result.x == -1)
1074 result.x = width_ - 1;
1075 }
1076 assert(0 <= result.x);
1077 assert(result.x < width_);
1078 assert(0 <= result.y);
1079 assert(result.y < height_);
1080 return result;
1081 }
bl_n(const FCoords & f)1082 inline FCoords Map::bl_n(const FCoords& f) const {
1083 assert(0 <= f.x);
1084 assert(f.x < width_);
1085 assert(0 <= f.y);
1086 assert(f.y < height_);
1087 assert(fields_.get() <= f.field);
1088 assert(f.field < fields_.get() + max_index());
1089 FCoords result(Coords(f.x, f.y + 1), f.field + width_);
1090 if (result.y == height_) {
1091 result.y = 0;
1092 result.field -= max_index();
1093 }
1094 if (result.y & 1) {
1095 --result.x;
1096 --result.field;
1097 if (result.x == -1) {
1098 result.x = width_ - 1;
1099 result.field += width_;
1100 }
1101 }
1102 assert(0 <= result.x);
1103 assert(result.x < width_);
1104 assert(0 <= result.y);
1105 assert(result.y < height_);
1106 assert(fields_.get() <= result.field);
1107 assert(result.field < fields_.get() + max_index());
1108 return result;
1109 }
1110
1111 // bottom-right: even: 0/+1 odd: +1/+1
get_brn(const Coords & f,Coords * const o)1112 inline void Map::get_brn(const Coords& f, Coords* const o) const {
1113 assert(0 <= f.x);
1114 assert(f.x < width_);
1115 assert(0 <= f.y);
1116 assert(f.y < height_);
1117 o->x = f.x;
1118 if (f.y & 1) {
1119 ++o->x;
1120 if (o->x == width_)
1121 o->x = 0;
1122 }
1123 o->y = f.y + 1;
1124 if (o->y == height_)
1125 o->y = 0;
1126 assert(0 <= o->x);
1127 assert(o->x < width_);
1128 assert(0 <= o->y);
1129 assert(o->y < height_);
1130 }
1131
get_brn(const FCoords & f,FCoords * const o)1132 inline void Map::get_brn(const FCoords& f, FCoords* const o) const {
1133 assert(0 <= f.x);
1134 assert(f.x < width_);
1135 assert(0 <= f.y);
1136 assert(f.y < height_);
1137 assert(fields_.get() <= f.field);
1138 assert(f.field < fields_.get() + max_index());
1139 o->x = f.x;
1140 o->field = f.field + width_;
1141 if (f.y & 1) {
1142 ++o->x;
1143 ++o->field;
1144 if (o->x == width_) {
1145 o->x = 0;
1146 o->field -= width_;
1147 }
1148 }
1149 o->y = f.y + 1;
1150 if (o->y == height_) {
1151 o->y = 0;
1152 o->field -= max_index();
1153 }
1154 assert(0 <= o->x);
1155 assert(o->x < width_);
1156 assert(0 <= o->y);
1157 assert(o->y < height_);
1158 assert(fields_.get() <= o->field);
1159 assert(o->field < fields_.get() + max_index());
1160 }
br_n(const Coords & f)1161 inline Coords Map::br_n(const Coords& f) const {
1162 assert(0 <= f.x);
1163 assert(f.x < width_);
1164 assert(0 <= f.y);
1165 assert(f.y < height_);
1166 Coords result(f.x, f.y + 1);
1167 if (f.y & 1) {
1168 ++result.x;
1169 if (result.x == width_)
1170 result.x = 0;
1171 }
1172 if (result.y == height_)
1173 result.y = 0;
1174 assert(0 <= result.x);
1175 assert(result.x < width_);
1176 assert(0 <= result.y);
1177 assert(result.y < height_);
1178 return result;
1179 }
br_n(const FCoords & f)1180 inline FCoords Map::br_n(const FCoords& f) const {
1181 assert(0 <= f.x);
1182 assert(f.x < width_);
1183 assert(0 <= f.y);
1184 assert(f.y < height_);
1185 assert(fields_.get() <= f.field);
1186 assert(f.field < fields_.get() + max_index());
1187 FCoords result(Coords(f.x, f.y + 1), f.field + width_);
1188 if (f.y & 1) {
1189 ++result.x;
1190 ++result.field;
1191 if (result.x == width_) {
1192 result.x = 0;
1193 result.field -= width_;
1194 }
1195 }
1196 if (result.y == height_) {
1197 result.y = 0;
1198 result.field -= max_index();
1199 }
1200 assert(0 <= result.x);
1201 assert(result.x < width_);
1202 assert(0 <= result.y);
1203 assert(result.y < height_);
1204 assert(fields_.get() <= result.field);
1205 assert(result.field < fields_.get() + max_index());
1206 return result;
1207 }
1208
get_neighbour(const FCoords & f,const Direction dir)1209 inline FCoords Map::get_neighbour(const FCoords& f, const Direction dir) const {
1210 switch (dir) {
1211 case WALK_NW:
1212 return tl_n(f);
1213 case WALK_NE:
1214 return tr_n(f);
1215 case WALK_E:
1216 return r_n(f);
1217 case WALK_SE:
1218 return br_n(f);
1219 case WALK_SW:
1220 return bl_n(f);
1221 case WALK_W:
1222 return l_n(f);
1223 default:
1224 NEVER_HERE();
1225 }
1226 }
1227
move_r(const int16_t mapwidth,FCoords & f)1228 inline void move_r(const int16_t mapwidth, FCoords& f) {
1229 assert(f.x < mapwidth);
1230 ++f.x;
1231 ++f.field;
1232 if (f.x == mapwidth) {
1233 f.x = 0;
1234 f.field -= mapwidth;
1235 }
1236 assert(f.x < mapwidth);
1237 }
1238
move_r(int16_t const mapwidth,FCoords & f,MapIndex & i)1239 inline void move_r(int16_t const mapwidth, FCoords& f, MapIndex& i) {
1240 assert(f.x < mapwidth);
1241 ++f.x;
1242 ++f.field;
1243 ++i;
1244 if (f.x == mapwidth) {
1245 f.x = 0;
1246 f.field -= mapwidth;
1247 i -= mapwidth;
1248 }
1249 assert(f.x < mapwidth);
1250 }
1251
1252 #define iterate_Map_FCoords(map, extent, fc) \
1253 for (Widelands::FCoords fc = (map).get_fcoords(Widelands::Coords(0, 0)); \
1254 fc.y < static_cast<int16_t>(extent.h); ++fc.y) \
1255 for (fc.x = 0; fc.x < static_cast<int16_t>(extent.w); ++fc.x, ++fc.field)
1256 } // namespace Widelands
1257
1258 #endif // end of include guard: WL_LOGIC_MAP_H
1259