map.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "debug.h"
00014 #include "core/alloc_func.hpp"
00015 #include "settings_type.h"
00016 #include "tile_map.h"
00017 #include "water_map.h"
00018 
00019 #if defined(_MSC_VER)
00020 /* Why the hell is that not in all MSVC headers?? */
00021 extern "C" _CRTIMP void __cdecl _assert(void *, void *, unsigned);
00022 #endif
00023 
00024 uint _map_log_x;     
00025 uint _map_log_y;     
00026 uint _map_size_x;    
00027 uint _map_size_y;    
00028 uint _map_size;      
00029 uint _map_tile_mask; 
00030 
00031 Tile *_m = NULL;          
00032 TileExtended *_me = NULL; 
00033 TileHeightData *_map_heightdata = NULL; 
00034 
00035 
00056 void AllocateMap(uint size_x, uint size_y, bool allocate_map_heightdata)
00057 {
00058   DEBUG(map, 2, "Min/max map size %d/%d, max map tiles %d", MIN_MAP_SIZE, MAX_MAP_SIZE, MAX_MAP_TILES);
00059   DEBUG(map, 1, "Allocating map of size %dx%d", size_x, size_y);
00060 
00061   /* Make sure that the map size is within the limits and that
00062    * size of both axes is a power of 2. */
00063   if (size_x * size_y > MAX_MAP_TILES ||
00064       size_x < MIN_MAP_SIZE ||
00065       size_y < MIN_MAP_SIZE ||
00066       (size_x & (size_x - 1)) != 0 ||
00067       (size_y & (size_y - 1)) != 0) {
00068     error("Invalid map size");
00069   }
00070 
00071   _map_log_x = FindFirstBit(size_x);
00072   _map_log_y = FindFirstBit(size_y);
00073   _map_size_x = size_x;
00074   _map_size_y = size_y;
00075   _map_size = size_x * size_y;
00076   _map_tile_mask = _map_size - 1;
00077 
00078   free(_m);
00079   free(_me);
00080 
00081   _m = CallocT<Tile>(_map_size);
00082   _me = CallocT<TileExtended>(_map_size);
00083 
00084   if (allocate_map_heightdata && AllowMoreHeightlevels()) {
00085     _map_heightdata = ReallocT<TileHeightData>(_map_heightdata, _map_size);
00086   }
00087 }
00088 
00096 void CopyHeightlevelDataFromOldToExtended()
00097 {
00098   _map_heightdata = ReallocT<TileHeightData>(_map_heightdata, MapSize());
00099 
00100   for (TileIndex i = 0; i < MapSize(); i++) {
00101     _map_heightdata[i].heightlevel = GB(_m[i].type_height, 0, 4);
00102   }
00103 }
00104 
00115 bool CopyHeightlevelDataFromExtendedToOld()
00116 {
00117   for (TileIndex i = 0; i < MapSize(); i++) {
00118     uint heightlevel = _map_heightdata[i].heightlevel;
00119 
00120     if (heightlevel >= 16) {
00121       /* We can't encode the heightlevel data from the extended array
00122        * in an array of 4 bit values. */
00123       return false;
00124     } else {
00125       SB(_m[i].type_height, 0, 4, heightlevel);
00126     }
00127   }
00128 
00129   free(_map_heightdata);
00130   _map_heightdata = NULL;
00131 
00132   return true;
00133 }
00134 
00135 #ifdef _DEBUG
00136 TileIndex TileAdd(TileIndex tile, TileIndexDiff add,
00137   const char *exp, const char *file, int line)
00138 {
00139   int dx;
00140   int dy;
00141   uint x;
00142   uint y;
00143 
00144   dx = add & MapMaxX();
00145   if (dx >= (int)MapSizeX() / 2) dx -= MapSizeX();
00146   dy = (add - dx) / (int)MapSizeX();
00147 
00148   x = TileX(tile) + dx;
00149   y = TileY(tile) + dy;
00150 
00151   if (x >= MapSizeX() || y >= MapSizeY()) {
00152     char buf[512];
00153 
00154     snprintf(buf, lengthof(buf), "TILE_ADD(%s) when adding 0x%.4X and 0x%.4X failed",
00155       exp, tile, add);
00156 #if !defined(_MSC_VER) || defined(WINCE)
00157     fprintf(stderr, "%s:%d %s\n", file, line, buf);
00158 #else
00159     _assert(buf, (char*)file, line);
00160 #endif
00161   }
00162 
00163   assert(TileXY(x, y) == TILE_MASK(tile + add));
00164 
00165   return TileXY(x, y);
00166 }
00167 #endif
00168 
00182 TileIndex TileAddWrap(TileIndex tile, int addx, int addy)
00183 {
00184   uint x = TileX(tile) + addx;
00185   uint y = TileY(tile) + addy;
00186 
00187   /* Disallow void tiles at the north border. */
00188   if (_settings_game.construction.freeform_edges && (x == 0 || y == 0)) return INVALID_TILE;
00189 
00190   /* Are we about to wrap? */
00191   if (x < MapMaxX() && y < MapMaxY()) return tile + TileDiffXY(addx, addy);
00192 
00193   return INVALID_TILE;
00194 }
00195 
00197 extern const TileIndexDiffC _tileoffs_by_diagdir[] = {
00198   {-1,  0}, 
00199   { 0,  1}, 
00200   { 1,  0}, 
00201   { 0, -1}  
00202 };
00203 
00205 extern const TileIndexDiffC _tileoffs_by_dir[] = {
00206   {-1, -1}, 
00207   {-1,  0}, 
00208   {-1,  1}, 
00209   { 0,  1}, 
00210   { 1,  1}, 
00211   { 1,  0}, 
00212   { 1, -1}, 
00213   { 0, -1}  
00214 };
00215 
00225 uint DistanceManhattan(TileIndex t0, TileIndex t1)
00226 {
00227   const uint dx = Delta(TileX(t0), TileX(t1));
00228   const uint dy = Delta(TileY(t0), TileY(t1));
00229   return dx + dy;
00230 }
00231 
00232 
00242 uint DistanceSquare(TileIndex t0, TileIndex t1)
00243 {
00244   const int dx = TileX(t0) - TileX(t1);
00245   const int dy = TileY(t0) - TileY(t1);
00246   return dx * dx + dy * dy;
00247 }
00248 
00249 
00257 uint DistanceMax(TileIndex t0, TileIndex t1)
00258 {
00259   const uint dx = Delta(TileX(t0), TileX(t1));
00260   const uint dy = Delta(TileY(t0), TileY(t1));
00261   return max(dx, dy);
00262 }
00263 
00264 
00273 uint DistanceMaxPlusManhattan(TileIndex t0, TileIndex t1)
00274 {
00275   const uint dx = Delta(TileX(t0), TileX(t1));
00276   const uint dy = Delta(TileY(t0), TileY(t1));
00277   return dx > dy ? 2 * dx + dy : 2 * dy + dx;
00278 }
00279 
00285 uint DistanceFromEdge(TileIndex tile)
00286 {
00287   const uint xl = TileX(tile);
00288   const uint yl = TileY(tile);
00289   const uint xh = MapSizeX() - 1 - xl;
00290   const uint yh = MapSizeY() - 1 - yl;
00291   const uint minl = min(xl, yl);
00292   const uint minh = min(xh, yh);
00293   return min(minl, minh);
00294 }
00295 
00302 uint DistanceFromEdgeDir(TileIndex tile, DiagDirection dir)
00303 {
00304   switch (dir) {
00305     case DIAGDIR_NE: return             TileX(tile) - (_settings_game.construction.freeform_edges ? 1 : 0);
00306     case DIAGDIR_NW: return             TileY(tile) - (_settings_game.construction.freeform_edges ? 1 : 0);
00307     case DIAGDIR_SW: return MapMaxX() - TileX(tile) - 1;
00308     case DIAGDIR_SE: return MapMaxY() - TileY(tile) - 1;
00309     default: NOT_REACHED();
00310   }
00311 }
00312 
00326 bool CircularTileSearch(TileIndex *tile, uint size, TestTileOnSearchProc proc, void *user_data)
00327 {
00328   assert(proc != NULL);
00329   assert(size > 0);
00330 
00331   if (size % 2 == 1) {
00332     /* If the length of the side is uneven, the center has to be checked
00333      * separately, as the pattern of uneven sides requires to go around the center */
00334     if (proc(*tile, user_data)) return true;
00335 
00336     /* If tile test is not successful, get one tile up,
00337      * ready for a test in first circle around center tile */
00338     *tile = TILE_ADD(*tile, TileOffsByDir(DIR_N));
00339     return CircularTileSearch(tile, size / 2, 1, 1, proc, user_data);
00340   } else {
00341     return CircularTileSearch(tile, size / 2, 0, 0, proc, user_data);
00342   }
00343 }
00344 
00364 bool CircularTileSearch(TileIndex *tile, uint radius, uint w, uint h, TestTileOnSearchProc proc, void *user_data)
00365 {
00366   assert(proc != NULL);
00367   assert(radius > 0);
00368 
00369   uint x = TileX(*tile) + w + 1;
00370   uint y = TileY(*tile);
00371 
00372   const uint extent[DIAGDIR_END] = { w, h, w, h };
00373 
00374   for (uint n = 0; n < radius; n++) {
00375     for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) {
00376       /* Is the tile within the map? */
00377       for (uint j = extent[dir] + n * 2 + 1; j != 0; j--) {
00378         if (x < MapSizeX() && y < MapSizeY()) {
00379           TileIndex t = TileXY(x, y);
00380           /* Is the callback successful? */
00381           if (proc(t, user_data)) {
00382             /* Stop the search */
00383             *tile = t;
00384             return true;
00385           }
00386         }
00387 
00388         /* Step to the next 'neighbour' in the circular line */
00389         x += _tileoffs_by_diagdir[dir].x;
00390         y += _tileoffs_by_diagdir[dir].y;
00391       }
00392     }
00393     /* Jump to next circle to test */
00394     x += _tileoffs_by_dir[DIR_W].x;
00395     y += _tileoffs_by_dir[DIR_W].y;
00396   }
00397 
00398   *tile = INVALID_TILE;
00399   return false;
00400 }
00401 
00408 uint GetClosestWaterDistance(TileIndex tile, bool water)
00409 {
00410   if (HasTileWaterGround(tile) == water) return 0;
00411 
00412   uint max_dist = water ? 0x7F : 0x200;
00413 
00414   int x = TileX(tile);
00415   int y = TileY(tile);
00416 
00417   uint max_x = MapMaxX();
00418   uint max_y = MapMaxY();
00419   uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
00420 
00421   /* go in a 'spiral' with increasing manhattan distance in each iteration */
00422   for (uint dist = 1; dist < max_dist; dist++) {
00423     /* next 'diameter' */
00424     y--;
00425 
00426     /* going counter-clockwise around this square */
00427     for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) {
00428       static const int8 ddx[DIAGDIR_END] = { -1,  1,  1, -1};
00429       static const int8 ddy[DIAGDIR_END] = {  1,  1, -1, -1};
00430 
00431       int dx = ddx[dir];
00432       int dy = ddy[dir];
00433 
00434       /* each side of this square has length 'dist' */
00435       for (uint a = 0; a < dist; a++) {
00436         /* MP_VOID tiles are not checked (interval is [min; max) for IsInsideMM())*/
00437         if (IsInsideMM(x, min_xy, max_x) && IsInsideMM(y, min_xy, max_y)) {
00438           TileIndex t = TileXY(x, y);
00439           if (HasTileWaterGround(t) == water) return dist;
00440         }
00441         x += dx;
00442         y += dy;
00443       }
00444     }
00445   }
00446 
00447   if (!water) {
00448     /* no land found - is this a water-only map? */
00449     for (TileIndex t = 0; t < MapSize(); t++) {
00450       if (!IsTileType(t, MP_VOID) && !IsTileType(t, MP_WATER)) return 0x1FF;
00451     }
00452   }
00453 
00454   return max_dist;
00455 }