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