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_FIELD_H
21 #define WL_LOGIC_FIELD_H
22 
23 #include <cassert>
24 
25 #include "base/wexception.h"
26 #include "graphic/playercolor.h"
27 #include "graphic/road_segments.h"
28 #include "logic/map_objects/walkingdir.h"
29 #include "logic/nodecaps.h"
30 #include "logic/widelands.h"
31 #include "logic/widelands_geometry.h"
32 
33 namespace Widelands {
34 
35 #define MAX_FIELD_HEIGHT 60
36 #define MAX_FIELD_HEIGHT_DIFF 5
37 
38 // Think, if we shouldn't call for each field a new() in map::set_size
39 // and a delete
40 // Okay, as it stands now, Field can be safely memset()ed to 0.
41 //
42 // No. In fact, you should view Field more as a plain old structure rather than
43 // a class. If you think of Fields as a class you get into a whole lot of
44 // problems (like the 6 neighbour field pointers we used to have). - Nicolai
45 // Probably it would be better to make Field a struct, rather than a class
46 // (things wouldn't change much, it's just a question of style and code
47 // understanding)
48 // Making it a struct doesn't add anything. struct is used interchangeably with
49 // class all around the code
50 
51 class Bob;
52 struct BaseImmovable;
53 
54 // Field is used so often, make sure it is as small as possible.
55 #pragma pack(push, 1)
56 /// a field like it is represented in the game
57 // TODO(unknown): This is all one evil hack :(
58 struct Field {
59 	friend class Map;
60 	friend class Bob;
61 	friend struct BaseImmovable;
62 
63 	enum BuildhelpIndex {
64 		Buildhelp_Flag = 0,
65 		Buildhelp_Small = 1,
66 		Buildhelp_Medium = 2,
67 		Buildhelp_Big = 3,
68 		Buildhelp_Mine = 4,
69 		Buildhelp_Port = 5,
70 		Buildhelp_None = 6
71 	};
72 
73 	using Height = uint8_t;
74 	using ResourceAmount = uint8_t;
75 
76 	struct Terrains {
77 		DescriptionIndex d, r;
78 	};
79 	static_assert(sizeof(Terrains) == sizeof(DescriptionIndex) * 2,
80 	              "assert(sizeof(Terrains) == sizeof(DescriptionIndex) * 2) failed.");
81 	struct Resources {
82 		DescriptionIndex d : 4, r : 4;
83 	};
84 #ifndef WIN32
85 	static_assert(sizeof(Resources) == sizeof(DescriptionIndex) / 2,
86 	              "assert(sizeof(Resources) == sizeof(DescriptionIndex) / 2) failed.");
87 #else
88 	static_assert(sizeof(Resources) == sizeof(DescriptionIndex),
89 	              "assert(sizeof(Resources) == sizeof(DescriptionIndex)) failed.");
90 #endif
91 	struct ResourceAmounts {
92 		ResourceAmount d : 4, r : 4;
93 	};
94 	static_assert(sizeof(ResourceAmounts) == 1, "assert(sizeof(ResourceAmounts) == 1) failed.");
95 
get_heightField96 	Height get_height() const {
97 		return height;
98 	}
nodecapsField99 	NodeCaps nodecaps() const {
100 		return static_cast<NodeCaps>(caps);
101 	}
maxcapsField102 	NodeCaps maxcaps() const {
103 		return static_cast<NodeCaps>(max_caps);
104 	}
get_capsField105 	uint16_t get_caps() const {
106 		return caps;
107 	}
108 
get_terrainsField109 	Terrains get_terrains() const {
110 		return terrains;
111 	}
112 	// The terrain on the downward triangle
terrain_dField113 	DescriptionIndex terrain_d() const {
114 		return terrains.d;
115 	}
116 	// The terrain on the triangle to the right
terrain_rField117 	DescriptionIndex terrain_r() const {
118 		return terrains.r;
119 	}
set_terrainsField120 	void set_terrains(const Terrains& i) {
121 		terrains = i;
122 	}
set_terrainField123 	void set_terrain(const TriangleIndex& t, DescriptionIndex const i)
124 
125 	{
126 		if (t == TriangleIndex::D)
127 			set_terrain_d(i);
128 		else
129 			set_terrain_r(i);
130 	}
set_terrain_dField131 	void set_terrain_d(DescriptionIndex const i) {
132 		terrains.d = i;
133 	}
set_terrain_rField134 	void set_terrain_r(DescriptionIndex const i) {
135 		terrains.r = i;
136 	}
137 
get_first_bobField138 	Bob* get_first_bob() const {
139 		return bobs;
140 	}
get_immovableField141 	const BaseImmovable* get_immovable() const {
142 		return immovable;
143 	}
get_immovableField144 	BaseImmovable* get_immovable() {
145 		return immovable;
146 	}
147 
148 	void set_brightness(int32_t l, int32_t r, int32_t tl, int32_t tr, int32_t bl, int32_t br);
get_brightnessField149 	int8_t get_brightness() const {
150 		return brightness;
151 	}
152 
153 	/**
154 	 * Does not change the border bit of this or neighbouring fields. That must
155 	 * be done separately.
156 	 */
set_owned_byField157 	void set_owned_by(const PlayerNumber n) {
158 		assert(n <= kMaxPlayers);
159 		owner_info_and_selections = n | (owner_info_and_selections & ~Player_Number_Bitmask);
160 	}
161 
get_owned_byField162 	PlayerNumber get_owned_by() const {
163 		assert((owner_info_and_selections & Player_Number_Bitmask) <= kMaxPlayers);
164 		return owner_info_and_selections & Player_Number_Bitmask;
165 	}
is_borderField166 	bool is_border() const {
167 		return owner_info_and_selections & Border_Bitmask;
168 	}
169 
170 	///
171 	/// Returns true when the node is owned by player_number and is not a border
172 	/// node. This is fast; only one compare (and a mask because the byte is
173 	/// shared with selection).
174 	///
175 	/// player_number must be in the range 1 .. Player_Number_Bitmask or the
176 	/// behaviour is undefined.
is_interiorField177 	bool is_interior(const PlayerNumber player_number) const {
178 		assert(0 < player_number);
179 		assert(player_number <= Player_Number_Bitmask);
180 		return player_number == (owner_info_and_selections & Owner_Info_Bitmask);
181 	}
182 
set_borderField183 	void set_border(const bool b) {
184 		owner_info_and_selections = (owner_info_and_selections & ~Border_Bitmask) | (b << Border_Bit);
185 	}
186 
get_roadField187 	RoadSegment get_road(uint8_t dir) const {
188 		switch (dir) {
189 		case WalkingDir::WALK_E:
190 			return road_east;
191 		case WalkingDir::WALK_SE:
192 			return road_southeast;
193 		case WalkingDir::WALK_SW:
194 			return road_southwest;
195 		default:
196 			throw wexception("Queried road going in invalid direction %i", dir);
197 		}
198 	}
set_roadField199 	void set_road(uint8_t dir, RoadSegment type) {
200 		switch (dir) {
201 		case WalkingDir::WALK_E:
202 			road_east = type;
203 			break;
204 		case WalkingDir::WALK_SE:
205 			road_southeast = type;
206 			break;
207 		case WalkingDir::WALK_SW:
208 			road_southwest = type;
209 			break;
210 		default:
211 			throw wexception("Attempt to set road going in invalid direction %i", dir);
212 		}
213 	}
214 
215 	// Resources can be set through Map::set_resources()
216 	// TODO(unknown): This should return DescriptionIndex
get_resourcesField217 	DescriptionIndex get_resources() const {
218 		return resources;
219 	}
get_resources_amountField220 	ResourceAmount get_resources_amount() const {
221 		return res_amount;
222 	}
223 	// TODO(unknown): This should return uint8_t
get_initial_res_amountField224 	ResourceAmount get_initial_res_amount() const {
225 		return initial_res_amount;
226 	}
227 
228 	/// \note you must reset this field's + neighbor's brightness when you
229 	/// change the height. Map::change_height does this. This function is not
230 	/// private, because the loader will use them directly But realize, most of
231 	/// the times you will need Map::set_field_height().
set_heightField232 	void set_height(Height const h) {
233 		height = static_cast<int8_t>(h) < 0 ? 0 : MAX_FIELD_HEIGHT < h ? MAX_FIELD_HEIGHT : h;
234 	}
235 
236 private:
237 	/**
238 	 * A field can be selected in one of 2 selections. This allows the user to
239 	 * use selection tools to select a set of fields and then perform a command
240 	 * on those fields.
241 	 *
242 	 * Selections can be edited with some operations. A selection can be
243 	 * 1. inverted,
244 	 * 2. unioned with the other selection,
245 	 * 3. intersected with the other selection or
246 	 * 4. differenced with the other selection.
247 	 *
248 	 * Each field can be owned by a player.
249 	 * The 2 highest bits are selected_a and selected_b.
250 	 * The next highest bit is the border bit.
251 	 * The low bits are the player number of the owner.
252 	 */
253 	using OwnerInfoAndSelectionsType = PlayerNumber;
254 	static const uint8_t Border_Bit = std::numeric_limits<OwnerInfoAndSelectionsType>::digits - 1;
255 	static const OwnerInfoAndSelectionsType Border_Bitmask = 1 << Border_Bit;
256 	static const OwnerInfoAndSelectionsType Player_Number_Bitmask = Border_Bitmask - 1;
257 	static const OwnerInfoAndSelectionsType Owner_Info_Bitmask =
258 	   Player_Number_Bitmask + Border_Bitmask;
259 	static_assert(kMaxPlayers <= Player_Number_Bitmask, "Bitmask is too big.");
260 
261 	// Data Members. Initialize everything to make cppcheck happy.
262 	/** linked list, \sa Bob::linknext_ */
263 	Bob* bobs = nullptr;
264 	BaseImmovable* immovable = nullptr;
265 
266 	uint8_t caps = 0U;
267 	uint8_t max_caps = 0U;
268 
269 	RoadSegment road_east = RoadSegment::kNone;
270 	RoadSegment road_southeast = RoadSegment::kNone;
271 	RoadSegment road_southwest = RoadSegment::kNone;
272 
273 	Height height = 0U;
274 	int8_t brightness = 0;
275 
276 	OwnerInfoAndSelectionsType owner_info_and_selections = Widelands::neutral();
277 
278 	DescriptionIndex resources = INVALID_INDEX;  ///< Resource type on this field, if any
279 	ResourceAmount initial_res_amount = 0U;      ///< Initial amount of resources
280 	ResourceAmount res_amount = 0U;              ///< Current amount of resources
281 
282 	Terrains terrains = Terrains{INVALID_INDEX, INVALID_INDEX};
283 };
284 #pragma pack(pop)
285 
286 // Check that Field is tightly packed.
287 static_assert(sizeof(Field) ==
288                  sizeof(void*) * 2 + sizeof(RoadSegment) * 3 + sizeof(DescriptionIndex) * 3 +
289                     sizeof(uint8_t) * 7,
290               "Field is not tightly packed.");
291 }  // namespace Widelands
292 
293 #endif  // end of include guard: WL_LOGIC_FIELD_H
294