smallmap_gui.cpp

Go to the documentation of this file.
00001 /* $Id: smallmap_gui.cpp 19104 2010-02-12 13:46:41Z alberth $ */
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 "clear_map.h"
00014 #include "industry.h"
00015 #include "station_map.h"
00016 #include "landscape.h"
00017 #include "window_gui.h"
00018 #include "tree_map.h"
00019 #include "viewport_func.h"
00020 #include "town.h"
00021 #include "blitter/factory.hpp"
00022 #include "tunnelbridge_map.h"
00023 #include "strings_func.h"
00024 #include "core/endian_func.hpp"
00025 #include "vehicle_base.h"
00026 #include "sound_func.h"
00027 #include "window_func.h"
00028 #include "company_base.h"
00029 
00030 #include "table/strings.h"
00031 #include "table/sprites.h"
00032 
00034 enum SmallMapWindowWidgets {
00035   SM_WIDGET_CAPTION,           
00036   SM_WIDGET_MAP_BORDER,        
00037   SM_WIDGET_MAP,               
00038   SM_WIDGET_LEGEND,            
00039   SM_WIDGET_ZOOM_IN,           
00040   SM_WIDGET_ZOOM_OUT,          
00041   SM_WIDGET_CONTOUR,           
00042   SM_WIDGET_VEHICLES,          
00043   SM_WIDGET_INDUSTRIES,        
00044   SM_WIDGET_ROUTES,            
00045   SM_WIDGET_VEGETATION,        
00046   SM_WIDGET_OWNERS,            
00047   SM_WIDGET_CENTERMAP,         
00048   SM_WIDGET_TOGGLETOWNNAME,    
00049   SM_WIDGET_SELECTINDUSTRIES,  
00050   SM_WIDGET_ENABLEINDUSTRIES,  
00051   SM_WIDGET_DISABLEINDUSTRIES, 
00052   SM_WIDGET_SHOW_HEIGHT,       
00053 };
00054 
00055 static int _smallmap_industry_count; 
00056 
00058 #define MK(a, b) {a, b, INVALID_INDUSTRYTYPE, true, false, false}
00059 
00060 #define MC(b) MK(0, b)
00061 
00062 #define MKEND() {0, STR_NULL, INVALID_INDUSTRYTYPE, true, true, false}
00063 
00065 #define MS(a, b) {a, b, INVALID_INDUSTRYTYPE, true, false, true}
00066 
00068 struct LegendAndColour {
00069   uint8 colour;      
00070   StringID legend;   
00071   IndustryType type; 
00072   bool show_on_map;  
00073   bool end;          
00074   bool col_break;    
00075 };
00076 
00078 static LegendAndColour _legend_land_contours[] = {
00079   /* The colours for the following values are set at BuildLandLegend() based on each colour scheme. */
00080   MC(STR_SMALLMAP_LEGENDA_100M),
00081   MC(STR_SMALLMAP_LEGENDA_200M),
00082   MC(STR_SMALLMAP_LEGENDA_300M),
00083   MC(STR_SMALLMAP_LEGENDA_400M),
00084   MC(STR_SMALLMAP_LEGENDA_500M),
00085 
00086   MS(0xD7, STR_SMALLMAP_LEGENDA_ROADS),
00087   MK(0x0A, STR_SMALLMAP_LEGENDA_RAILROADS),
00088   MK(0x98, STR_SMALLMAP_LEGENDA_STATIONS_AIRPORTS_DOCKS),
00089   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00090   MK(0x0F, STR_SMALLMAP_LEGENDA_VEHICLES),
00091   MKEND()
00092 };
00093 
00094 static const LegendAndColour _legend_vehicles[] = {
00095   MK(0xB8, STR_SMALLMAP_LEGENDA_TRAINS),
00096   MK(0xBF, STR_SMALLMAP_LEGENDA_ROAD_VEHICLES),
00097   MK(0x98, STR_SMALLMAP_LEGENDA_SHIPS),
00098   MK(0x0F, STR_SMALLMAP_LEGENDA_AIRCRAFT),
00099 
00100   MS(0xD7, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
00101   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00102   MKEND()
00103 };
00104 
00105 static const LegendAndColour _legend_routes[] = {
00106   MK(0xD7, STR_SMALLMAP_LEGENDA_ROADS),
00107   MK(0x0A, STR_SMALLMAP_LEGENDA_RAILROADS),
00108   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00109 
00110   MS(0x56, STR_SMALLMAP_LEGENDA_RAILROAD_STATION),
00111   MK(0xC2, STR_SMALLMAP_LEGENDA_TRUCK_LOADING_BAY),
00112   MK(0xBF, STR_SMALLMAP_LEGENDA_BUS_STATION),
00113   MK(0xB8, STR_SMALLMAP_LEGENDA_AIRPORT_HELIPORT),
00114   MK(0x98, STR_SMALLMAP_LEGENDA_DOCK),
00115   MKEND()
00116 };
00117 
00118 static const LegendAndColour _legend_vegetation[] = {
00119   MK(0x52, STR_SMALLMAP_LEGENDA_ROUGH_LAND),
00120   MK(0x54, STR_SMALLMAP_LEGENDA_GRASS_LAND),
00121   MK(0x37, STR_SMALLMAP_LEGENDA_BARE_LAND),
00122   MK(0x25, STR_SMALLMAP_LEGENDA_FIELDS),
00123   MK(0x57, STR_SMALLMAP_LEGENDA_TREES),
00124   MK(0xD0, STR_SMALLMAP_LEGENDA_FOREST),
00125 
00126   MS(0x0A, STR_SMALLMAP_LEGENDA_ROCKS),
00127   MK(0xC2, STR_SMALLMAP_LEGENDA_DESERT),
00128   MK(0x98, STR_SMALLMAP_LEGENDA_SNOW),
00129   MK(0xD7, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
00130   MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00131   MKEND()
00132 };
00133 
00134 static const LegendAndColour _legend_land_owners[] = {
00135   MK(0xCA, STR_SMALLMAP_LEGENDA_WATER),
00136   MK(0x54, STR_SMALLMAP_LEGENDA_NO_OWNER),
00137   MK(0xB4, STR_SMALLMAP_LEGENDA_TOWNS),
00138   MK(0x20, STR_SMALLMAP_LEGENDA_INDUSTRIES),
00139   MKEND()
00140 };
00141 #undef MK
00142 #undef MC
00143 #undef MS
00144 #undef MKEND
00145 
00148 static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1];
00149 /* For connecting industry type to position in industries list(small map legend) */
00150 static uint _industry_to_list_pos[NUM_INDUSTRYTYPES];
00152 static bool _smallmap_industry_show_heightmap;
00153 
00157 void BuildIndustriesLegend()
00158 {
00159   uint j = 0;
00160 
00161   /* Add each name */
00162   for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
00163     const IndustrySpec *indsp = GetIndustrySpec(i);
00164     if (indsp->enabled) {
00165       _legend_from_industries[j].legend = indsp->name;
00166       _legend_from_industries[j].colour = indsp->map_colour;
00167       _legend_from_industries[j].type = i;
00168       _legend_from_industries[j].show_on_map = true;
00169       _legend_from_industries[j].col_break = false;
00170       _legend_from_industries[j].end = false;
00171 
00172       /* Store widget number for this industry type */
00173       _industry_to_list_pos[i] = j;
00174       j++;
00175     }
00176   }
00177   /* Terminate the list */
00178   _legend_from_industries[j].end = true;
00179 
00180   /* Store number of enabled industries */
00181   _smallmap_industry_count = j;
00182 }
00183 
00184 static const LegendAndColour * const _legend_table[] = {
00185   _legend_land_contours,
00186   _legend_vehicles,
00187   _legend_from_industries,
00188   _legend_routes,
00189   _legend_vegetation,
00190   _legend_land_owners,
00191 };
00192 
00193 #define MKCOLOUR(x) TO_LE32X(x)
00194 
00196 static const uint32 _green_map_heights[] = {
00197   MKCOLOUR(0x5A5A5A5A),
00198   MKCOLOUR(0x5A5B5A5B),
00199   MKCOLOUR(0x5B5B5B5B),
00200   MKCOLOUR(0x5B5C5B5C),
00201   MKCOLOUR(0x5C5C5C5C),
00202   MKCOLOUR(0x5C5D5C5D),
00203   MKCOLOUR(0x5D5D5D5D),
00204   MKCOLOUR(0x5D5E5D5E),
00205   MKCOLOUR(0x5E5E5E5E),
00206   MKCOLOUR(0x5E5F5E5F),
00207   MKCOLOUR(0x5F5F5F5F),
00208   MKCOLOUR(0x5F1F5F1F),
00209   MKCOLOUR(0x1F1F1F1F),
00210   MKCOLOUR(0x1F271F27),
00211   MKCOLOUR(0x27272727),
00212   MKCOLOUR(0x27272727),
00213 };
00214 assert_compile(lengthof(_green_map_heights) == MAX_TILE_HEIGHT + 1);
00215 
00217 static const uint32 _dark_green_map_heights[] = {
00218   MKCOLOUR(0x60606060),
00219   MKCOLOUR(0x60616061),
00220   MKCOLOUR(0x61616161),
00221   MKCOLOUR(0x61626162),
00222   MKCOLOUR(0x62626262),
00223   MKCOLOUR(0x62636263),
00224   MKCOLOUR(0x63636363),
00225   MKCOLOUR(0x63646364),
00226   MKCOLOUR(0x64646464),
00227   MKCOLOUR(0x64656465),
00228   MKCOLOUR(0x65656565),
00229   MKCOLOUR(0x65666566),
00230   MKCOLOUR(0x66666666),
00231   MKCOLOUR(0x66676667),
00232   MKCOLOUR(0x67676767),
00233   MKCOLOUR(0x67676767),
00234 };
00235 assert_compile(lengthof(_dark_green_map_heights) == MAX_TILE_HEIGHT + 1);
00236 
00238 static const uint32 _violet_map_heights[] = {
00239   MKCOLOUR(0x80808080),
00240   MKCOLOUR(0x80818081),
00241   MKCOLOUR(0x81818181),
00242   MKCOLOUR(0x81828182),
00243   MKCOLOUR(0x82828282),
00244   MKCOLOUR(0x82838283),
00245   MKCOLOUR(0x83838383),
00246   MKCOLOUR(0x83848384),
00247   MKCOLOUR(0x84848484),
00248   MKCOLOUR(0x84858485),
00249   MKCOLOUR(0x85858585),
00250   MKCOLOUR(0x85868586),
00251   MKCOLOUR(0x86868686),
00252   MKCOLOUR(0x86878687),
00253   MKCOLOUR(0x87878787),
00254   MKCOLOUR(0x87878787),
00255 };
00256 assert_compile(lengthof(_violet_map_heights) == MAX_TILE_HEIGHT + 1);
00257 
00259 struct SmallMapColourScheme {
00260   const uint32 *height_colours; 
00261   uint32 default_colour;   
00262 };
00263 
00265 static const SmallMapColourScheme _heightmap_schemes[] = {
00266   {_green_map_heights,      MKCOLOUR(0x54545454)}, 
00267   {_dark_green_map_heights, MKCOLOUR(0x62626262)}, 
00268   {_violet_map_heights,     MKCOLOUR(0x82828282)}, 
00269 };
00270 
00271 void BuildLandLegend()
00272 {
00273   _legend_land_contours[0].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[0];
00274   _legend_land_contours[1].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[4];
00275   _legend_land_contours[2].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[8];
00276   _legend_land_contours[3].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[12];
00277   _legend_land_contours[4].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[14];
00278 }
00279 
00280 struct AndOr {
00281   uint32 mor;
00282   uint32 mand;
00283 };
00284 
00285 static inline uint32 ApplyMask(uint32 colour, const AndOr *mask)
00286 {
00287   return (colour & mask->mand) | mask->mor;
00288 }
00289 
00290 
00292 static const AndOr _smallmap_contours_andor[] = {
00293   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_CLEAR
00294   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)}, // MP_RAILWAY
00295   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_ROAD
00296   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_HOUSE
00297   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TREES
00298   {MKCOLOUR(0x98989898), MKCOLOUR(0x00000000)}, // MP_STATION
00299   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)}, // MP_WATER
00300   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_VOID
00301   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)}, // MP_INDUSTRY
00302   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TUNNELBRIDGE
00303   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_UNMOVABLE
00304   {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)},
00305 };
00306 
00308 static const AndOr _smallmap_vehicles_andor[] = {
00309   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_CLEAR
00310   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_RAILWAY
00311   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_ROAD
00312   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_HOUSE
00313   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TREES
00314   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)}, // MP_STATION
00315   {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)}, // MP_WATER
00316   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_VOID
00317   {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)}, // MP_INDUSTRY
00318   {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)}, // MP_TUNNELBRIDGE
00319   {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)}, // MP_UNMOVABLE
00320   {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
00321 };
00322 
00324 static const byte _tiletype_importance[] = {
00325   2, // MP_CLEAR
00326   8, // MP_RAILWAY
00327   7, // MP_ROAD
00328   5, // MP_HOUSE
00329   2, // MP_TREES
00330   9, // MP_STATION
00331   2, // MP_WATER
00332   1, // MP_VOID
00333   6, // MP_INDUSTRY
00334   8, // MP_TUNNELBRIDGE
00335   2, // MP_UNMOVABLE
00336   0,
00337 };
00338 
00339 
00340 static inline TileType GetEffectiveTileType(TileIndex tile)
00341 {
00342   TileType t = GetTileType(tile);
00343 
00344   if (t == MP_TUNNELBRIDGE) {
00345     TransportType tt = GetTunnelBridgeTransportType(tile);
00346 
00347     switch (tt) {
00348       case TRANSPORT_RAIL: t = MP_RAILWAY; break;
00349       case TRANSPORT_ROAD: t = MP_ROAD;    break;
00350       default:             t = MP_WATER;   break;
00351     }
00352   }
00353   return t;
00354 }
00355 
00362 static inline uint32 GetSmallMapContoursPixels(TileIndex tile, TileType t)
00363 {
00364   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00365   return ApplyMask(cs->height_colours[TileHeight(tile)], &_smallmap_contours_andor[t]);
00366 }
00367 
00375 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile, TileType t)
00376 {
00377   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00378   return ApplyMask(cs->default_colour, &_smallmap_vehicles_andor[t]);
00379 }
00380 
00388 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile, TileType t)
00389 {
00390   if (t == MP_INDUSTRY) {
00391     /* If industry is allowed to be seen, use its colour on the map */
00392     if (_legend_from_industries[_industry_to_list_pos[Industry::GetByTile(tile)->type]].show_on_map) {
00393       return GetIndustrySpec(Industry::GetByTile(tile)->type)->map_colour * 0x01010101;
00394     } else {
00395       /* Otherwise, return the colour of the clear tiles, which will make it disappear */
00396       t = MP_CLEAR;
00397     }
00398   }
00399 
00400   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00401   return ApplyMask(_smallmap_industry_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour, &_smallmap_vehicles_andor[t]);
00402 }
00403 
00411 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile, TileType t)
00412 {
00413   if (t == MP_STATION) {
00414     switch (GetStationType(tile)) {
00415       case STATION_RAIL:    return MKCOLOUR(0x56565656);
00416       case STATION_AIRPORT: return MKCOLOUR(0xB8B8B8B8);
00417       case STATION_TRUCK:   return MKCOLOUR(0xC2C2C2C2);
00418       case STATION_BUS:     return MKCOLOUR(0xBFBFBFBF);
00419       case STATION_DOCK:    return MKCOLOUR(0x98989898);
00420       default:              return MKCOLOUR(0xFFFFFFFF);
00421     }
00422   }
00423 
00424   /* Ground colour */
00425   const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00426   return ApplyMask(cs->default_colour, &_smallmap_contours_andor[t]);
00427 }
00428 
00429 
00430 static const uint32 _vegetation_clear_bits[] = {
00431   MKCOLOUR(0x54545454), 
00432   MKCOLOUR(0x52525252), 
00433   MKCOLOUR(0x0A0A0A0A), 
00434   MKCOLOUR(0x25252525), 
00435   MKCOLOUR(0x98989898), 
00436   MKCOLOUR(0xC2C2C2C2), 
00437   MKCOLOUR(0x54545454), 
00438   MKCOLOUR(0x54545454), 
00439 };
00440 
00448 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile, TileType t)
00449 {
00450   switch (t) {
00451     case MP_CLEAR:
00452       return (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) ? MKCOLOUR(0x37373737) : _vegetation_clear_bits[GetClearGround(tile)];
00453 
00454     case MP_INDUSTRY:
00455       return GetIndustrySpec(Industry::GetByTile(tile)->type)->check_proc == CHECK_FOREST ? MKCOLOUR(0xD0D0D0D0) : MKCOLOUR(0xB5B5B5B5);
00456 
00457     case MP_TREES:
00458       if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT || GetTreeGround(tile) == TREE_GROUND_ROUGH_SNOW) {
00459         return (_settings_game.game_creation.landscape == LT_ARCTIC) ? MKCOLOUR(0x98575798) : MKCOLOUR(0xC25757C2);
00460       }
00461       return MKCOLOUR(0x54575754);
00462 
00463     default:
00464       return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
00465   }
00466 }
00467 
00468 
00469 static uint32 _owner_colours[OWNER_END + 1];
00470 
00478 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile, TileType t)
00479 {
00480   Owner o;
00481 
00482   switch (t) {
00483     case MP_INDUSTRY: o = OWNER_END;          break;
00484     case MP_HOUSE:    o = OWNER_TOWN;         break;
00485     default:          o = GetTileOwner(tile); break;
00486     /* FIXME: For MP_ROAD there are multiple owners.
00487      * GetTileOwner returns the rail owner (level crossing) resp. the owner of ROADTYPE_ROAD (normal road),
00488      * even if there are no ROADTYPE_ROAD bits on the tile.
00489      */
00490   }
00491 
00492   return _owner_colours[o];
00493 }
00494 
00496 static const byte _vehicle_type_colours[6] = {
00497   184, 191, 152, 15, 215, 184
00498 };
00499 
00500 
00502 class SmallMapWindow : public Window {
00504   enum SmallMapType {
00505     SMT_CONTOUR,
00506     SMT_VEHICLES,
00507     SMT_INDUSTRY,
00508     SMT_ROUTES,
00509     SMT_VEGETATION,
00510     SMT_OWNER,
00511   };
00512 
00514   enum ZoomLevelChange {
00515     ZLC_INITIALIZE, 
00516     ZLC_ZOOM_OUT,   
00517     ZLC_ZOOM_IN,    
00518   };
00519 
00520   static SmallMapType map_type; 
00521   static bool show_towns;       
00522 
00523   static const uint LEGEND_BLOB_WIDTH = 8;              
00524   static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2; 
00525   uint min_number_of_columns;    
00526   uint min_number_of_fixed_rows; 
00527   uint column_width;             
00528 
00529   int32 scroll_x;  
00530   int32 scroll_y;  
00531   int32 subscroll; 
00532   int zoom;        
00533 
00534   static const uint8 FORCE_REFRESH_PERIOD = 0x1F; 
00535   uint8 refresh; 
00536 
00543   FORCEINLINE Point RemapTile(int tile_x, int tile_y) const
00544   {
00545     int x_offset = tile_x - this->scroll_x / TILE_SIZE;
00546     int y_offset = tile_y - this->scroll_y / TILE_SIZE;
00547 
00548     if (this->zoom == 1) return RemapCoords(x_offset, y_offset, 0);
00549 
00550     /* For negative offsets, round towards -inf. */
00551     if (x_offset < 0) x_offset -= this->zoom - 1;
00552     if (y_offset < 0) y_offset -= this->zoom - 1;
00553 
00554     return RemapCoords(x_offset / this->zoom, y_offset / this->zoom, 0);
00555   }
00556 
00567   FORCEINLINE Point PixelToTile(int px, int py, int *sub, bool add_sub = true) const
00568   {
00569     if (add_sub) px += this->subscroll;  // Total horizontal offset.
00570 
00571     /* For each two rows down, add a x and a y tile, and
00572      * For each four pixels to the right, move a tile to the right. */
00573     Point pt = {((py >> 1) - (px >> 2)) * this->zoom, ((py >> 1) + (px >> 2)) * this->zoom};
00574     px &= 3;
00575 
00576     if (py & 1) { // Odd number of rows, handle the 2 pixel shift.
00577       if (px < 2) {
00578         pt.x += this->zoom;
00579         px += 2;
00580       } else {
00581         pt.y += this->zoom;
00582         px -= 2;
00583       }
00584     }
00585 
00586     *sub = px;
00587     return pt;
00588   }
00589 
00599   Point ComputeScroll(int tx, int ty, int x, int y, int *sub)
00600   {
00601     assert(x >= 0 && y >= 0);
00602 
00603     int new_sub;
00604     Point tile_xy = PixelToTile(x, y, &new_sub, false);
00605     tx -= tile_xy.x;
00606     ty -= tile_xy.y;
00607 
00608     Point scroll;
00609     if (new_sub == 0) {
00610       *sub = 0;
00611       scroll.x = (tx + this->zoom) * TILE_SIZE;
00612       scroll.y = (ty - this->zoom) * TILE_SIZE;
00613     } else {
00614       *sub = 4 - new_sub;
00615       scroll.x = (tx + 2 * this->zoom) * TILE_SIZE;
00616       scroll.y = (ty - 2 * this->zoom) * TILE_SIZE;
00617     }
00618     return scroll;
00619   }
00620 
00626   void SetZoomLevel(ZoomLevelChange change, const Point *zoom_pt)
00627   {
00628     static const int zoomlevels[] = {1, 2, 4, 6, 8}; // Available zoom levels. Bigger number means more zoom-out (further away).
00629     static const int MIN_ZOOM_INDEX = 0;
00630     static const int MAX_ZOOM_INDEX = lengthof(zoomlevels) - 1;
00631 
00632     int new_index, cur_index, sub;
00633     Point tile;
00634     switch (change) {
00635       case ZLC_INITIALIZE:
00636         cur_index = - 1; // Definitely different from new_index.
00637         new_index = MIN_ZOOM_INDEX;
00638         break;
00639 
00640       case ZLC_ZOOM_IN:
00641       case ZLC_ZOOM_OUT:
00642         for (cur_index = MIN_ZOOM_INDEX; cur_index <= MAX_ZOOM_INDEX; cur_index++) {
00643           if (this->zoom == zoomlevels[cur_index]) break;
00644         }
00645         assert(cur_index <= MAX_ZOOM_INDEX);
00646 
00647         tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
00648         new_index = Clamp(cur_index + ((change == ZLC_ZOOM_IN) ? -1 : 1), MIN_ZOOM_INDEX, MAX_ZOOM_INDEX);
00649         break;
00650 
00651       default: NOT_REACHED();
00652     }
00653 
00654     if (new_index != cur_index) {
00655       this->zoom = zoomlevels[new_index];
00656       if (cur_index >= 0) {
00657         Point new_tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
00658         this->SetNewScroll(this->scroll_x + (tile.x - new_tile.x) * TILE_SIZE,
00659             this->scroll_y + (tile.y - new_tile.y) * TILE_SIZE, sub);
00660       }
00661       this->SetWidgetDisabledState(SM_WIDGET_ZOOM_IN,  this->zoom == zoomlevels[MIN_ZOOM_INDEX]);
00662       this->SetWidgetDisabledState(SM_WIDGET_ZOOM_OUT, this->zoom == zoomlevels[MAX_ZOOM_INDEX]);
00663       this->SetDirty();
00664     }
00665   }
00666 
00672   inline uint32 GetTileColours(const TileArea &ta) const
00673   {
00674     int importance = 0;
00675     TileIndex tile = INVALID_TILE; // Position of the most important tile.
00676     TileType et = MP_VOID;         // Effective tile type at that position.
00677 
00678     TILE_AREA_LOOP(ti, ta) {
00679       TileType ttype = GetEffectiveTileType(ti);
00680       if (_tiletype_importance[ttype] > importance) {
00681         importance = _tiletype_importance[ttype];
00682         tile = ti;
00683         et = ttype;
00684       }
00685     }
00686 
00687     switch (this->map_type) {
00688       case SMT_CONTOUR:
00689         return GetSmallMapContoursPixels(tile, et);
00690 
00691       case SMT_VEHICLES:
00692         return GetSmallMapVehiclesPixels(tile, et);
00693 
00694       case SMT_INDUSTRY:
00695         return GetSmallMapIndustriesPixels(tile, et);
00696 
00697       case SMT_ROUTES:
00698         return GetSmallMapRoutesPixels(tile, et);
00699 
00700       case SMT_VEGETATION:
00701         return GetSmallMapVegetationPixels(tile, et);
00702 
00703       case SMT_OWNER:
00704         return GetSmallMapOwnerPixels(tile, et);
00705 
00706       default: NOT_REACHED();
00707     }
00708   }
00709 
00724   void DrawSmallMapColumn(void *dst, uint xc, uint yc, int pitch, int reps, int start_pos, int end_pos, Blitter *blitter) const
00725   {
00726     void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
00727     uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
00728 
00729     do {
00730       /* Check if the tile (xc,yc) is within the map range */
00731       if (xc >= MapMaxX() || yc >= MapMaxY()) continue;
00732 
00733       /* Check if the dst pointer points to a pixel inside the screen buffer */
00734       if (dst < _screen.dst_ptr) continue;
00735       if (dst >= dst_ptr_abs_end) continue;
00736 
00737       /* Construct tilearea covered by (xc, yc, xc + this->zoom, yc + this->zoom) such that it is within min_xy limits. */
00738       TileArea ta;
00739       if (min_xy == 1 && (xc == 0 || yc == 0)) {
00740         if (this->zoom == 1) continue; // The tile area is empty, don't draw anything.
00741 
00742         ta = TileArea(TileXY(max(min_xy, xc), max(min_xy, yc)), this->zoom - (xc == 0), this->zoom - (yc == 0));
00743       } else {
00744         ta = TileArea(TileXY(xc, yc), this->zoom, this->zoom);
00745       }
00746       ta.ClampToMap(); // Clamp to map boundaries (may contain MP_VOID tiles!).
00747 
00748       uint32 val = this->GetTileColours(ta);
00749       uint8 *val8 = (uint8 *)&val;
00750       int idx = max(0, -start_pos);
00751       for (int pos = max(0, start_pos); pos < end_pos; pos++) {
00752         blitter->SetPixel(dst, idx, 0, val8[idx]);
00753         idx++;
00754       }
00755     /* Switch to next tile in the column */
00756     } while (xc += this->zoom, yc += this->zoom, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
00757   }
00758 
00764   void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
00765   {
00766     const Vehicle *v;
00767     FOR_ALL_VEHICLES(v) {
00768       if (v->type == VEH_EFFECT) continue;
00769       if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
00770 
00771       /* Remap into flat coordinates. */
00772       Point pt = this->RemapTile(v->x_pos / TILE_SIZE, v->y_pos / TILE_SIZE);
00773 
00774       int y = pt.y - dpi->top;
00775       if (!IsInsideMM(y, 0, dpi->height)) continue; // y is out of bounds.
00776 
00777       bool skip = false; // Default is to draw both pixels.
00778       int x = pt.x - this->subscroll - 3 - dpi->left; // Offset X coordinate.
00779       if (x < 0) {
00780         /* if x+1 is 0, that means we're on the very left edge,
00781          * and should thus only draw a single pixel */
00782         if (++x != 0) continue;
00783         skip = true;
00784       } else if (x >= dpi->width - 1) {
00785         /* Check if we're at the very right edge, and if so draw only a single pixel */
00786         if (x != dpi->width - 1) continue;
00787         skip = true;
00788       }
00789 
00790       /* Calculate pointer to pixel and the colour */
00791       byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : 0xF;
00792 
00793       /* And draw either one or two pixels depending on clipping */
00794       blitter->SetPixel(dpi->dst_ptr, x, y, colour);
00795       if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
00796     }
00797   }
00798 
00803   void DrawTowns(const DrawPixelInfo *dpi) const
00804   {
00805     const Town *t;
00806     FOR_ALL_TOWNS(t) {
00807       /* Remap the town coordinate */
00808       Point pt = this->RemapTile(TileX(t->xy), TileY(t->xy));
00809       int x = pt.x - this->subscroll - (t->sign.width_small >> 1);
00810       int y = pt.y;
00811 
00812       /* Check if the town sign is within bounds */
00813       if (x + t->sign.width_small > dpi->left &&
00814           x < dpi->left + dpi->width &&
00815           y + FONT_HEIGHT_SMALL > dpi->top &&
00816           y < dpi->top + dpi->height) {
00817         /* And draw it. */
00818         SetDParam(0, t->index);
00819         DrawString(x, x + t->sign.width_small, y, STR_SMALLMAP_TOWN);
00820       }
00821     }
00822   }
00823 
00830   static inline void DrawVertMapIndicator(int x, int y, int y2)
00831   {
00832     GfxFillRect(x, y,      x, y + 3, 69);
00833     GfxFillRect(x, y2 - 3, x, y2,    69);
00834   }
00835 
00842   static inline void DrawHorizMapIndicator(int x, int x2, int y)
00843   {
00844     GfxFillRect(x,      y, x + 3, y, 69);
00845     GfxFillRect(x2 - 3, y, x2,    y, 69);
00846   }
00847 
00851   void DrawMapIndicators() const
00852   {
00853     /* Find main viewport. */
00854     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
00855 
00856     Point tile = InverseRemapCoords(vp->virtual_left, vp->virtual_top);
00857     Point tl = this->RemapTile(tile.x >> 4, tile.y >> 4);
00858     tl.x -= this->subscroll;
00859 
00860     tile = InverseRemapCoords(vp->virtual_left + vp->virtual_width, vp->virtual_top + vp->virtual_height);
00861     Point br = this->RemapTile(tile.x >> 4, tile.y >> 4);
00862     br.x -= this->subscroll;
00863 
00864     SmallMapWindow::DrawVertMapIndicator(tl.x, tl.y, br.y);
00865     SmallMapWindow::DrawVertMapIndicator(br.x, tl.y, br.y);
00866 
00867     SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, tl.y);
00868     SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, br.y);
00869   }
00870 
00882   void DrawSmallMap(DrawPixelInfo *dpi) const
00883   {
00884     Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00885     DrawPixelInfo *old_dpi;
00886 
00887     old_dpi = _cur_dpi;
00888     _cur_dpi = dpi;
00889 
00890     /* Clear it */
00891     GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
00892 
00893     /* Setup owner table */
00894     if (this->map_type == SMT_OWNER) {
00895       const Company *c;
00896 
00897       /* Fill with some special colours */
00898       _owner_colours[OWNER_TOWN]  = MKCOLOUR(0xB4B4B4B4);
00899       _owner_colours[OWNER_NONE]  = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour;
00900       _owner_colours[OWNER_WATER] = MKCOLOUR(0xCACACACA);
00901       _owner_colours[OWNER_END]   = MKCOLOUR(0x20202020); // Industry
00902 
00903       /* Now fill with the company colours */
00904       FOR_ALL_COMPANIES(c) {
00905         _owner_colours[c->index] = _colour_gradient[c->colour][5] * 0x01010101;
00906       }
00907     }
00908 
00909     /* Which tile is displayed at (dpi->left, dpi->top)? */
00910     int dx;
00911     Point tile = this->PixelToTile(dpi->left, dpi->top, &dx);
00912     int tile_x = this->scroll_x / TILE_SIZE + tile.x;
00913     int tile_y = this->scroll_y / TILE_SIZE + tile.y;
00914 
00915     void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
00916     int x = - dx - 4;
00917     int y = 0;
00918 
00919     for (;;) {
00920       /* Distance from left edge */
00921       if (x >= -3) {
00922         if (x >= dpi->width) break; // Exit the loop.
00923 
00924         int end_pos = min(dpi->width, x + 4);
00925         int reps = (dpi->height - y + 1) / 2; // Number of lines.
00926         if (reps > 0) {
00927           this->DrawSmallMapColumn(ptr, tile_x, tile_y, dpi->pitch * 2, reps, x, end_pos, blitter);
00928         }
00929       }
00930 
00931       if (y == 0) {
00932         tile_y += this->zoom;
00933         y++;
00934         ptr = blitter->MoveTo(ptr, 0, 1);
00935       } else {
00936         tile_x -= this->zoom;
00937         y--;
00938         ptr = blitter->MoveTo(ptr, 0, -1);
00939       }
00940       ptr = blitter->MoveTo(ptr, 2, 0);
00941       x += 2;
00942     }
00943 
00944     /* Draw vehicles */
00945     if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter);
00946 
00947     /* Draw town names */
00948     if (this->show_towns) this->DrawTowns(dpi);
00949 
00950     /* Draw map indicators */
00951     this->DrawMapIndicators();
00952 
00953     _cur_dpi = old_dpi;
00954   }
00955 
00956 public:
00957   SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), refresh(FORCE_REFRESH_PERIOD)
00958   {
00959     this->InitNested(desc, window_number);
00960     this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
00961 
00962     _smallmap_industry_show_heightmap = false;
00963     BuildLandLegend();
00964     this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
00965 
00966     this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
00967     this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
00968 
00969     this->SetZoomLevel(ZLC_INITIALIZE, NULL);
00970     this->SmallMapCenterOnCurrentPos();
00971   }
00972 
00976   inline uint GetMaxLegendHeight() const
00977   {
00978     uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + this->min_number_of_columns - 1) / this->min_number_of_columns);
00979     return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
00980   }
00981 
00985   inline uint GetMinLegendWidth() const
00986   {
00987     return WD_FRAMERECT_LEFT + this->min_number_of_columns * this->column_width;
00988   }
00989 
00993   inline uint GetNumberColumnsLegend(uint width) const
00994   {
00995     return width / this->column_width;
00996   }
00997 
01001   uint GetLegendHeight(uint width) const
01002   {
01003     uint num_columns = this->GetNumberColumnsLegend(width);
01004     uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + num_columns - 1) / num_columns);
01005     return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
01006   }
01007 
01008   virtual void SetStringParameters(int widget) const
01009   {
01010     switch (widget) {
01011       case SM_WIDGET_CAPTION:
01012         SetDParam(0, STR_SMALLMAP_TYPE_CONTOURS + this->map_type);
01013         break;
01014     }
01015   }
01016 
01017   virtual void OnInit()
01018   {
01019     uint min_width = 0;
01020     this->min_number_of_columns = INDUSTRY_MIN_NUMBER_OF_COLUMNS;
01021     this->min_number_of_fixed_rows = 0;
01022     for (uint i = 0; i < lengthof(_legend_table); i++) {
01023       uint height = 0;
01024       uint num_columns = 1;
01025       for (const LegendAndColour *tbl = _legend_table[i]; !tbl->end; ++tbl) {
01026         StringID str;
01027         if (i == SMT_INDUSTRY) {
01028           SetDParam(0, tbl->legend);
01029           SetDParam(1, IndustryPool::MAX_SIZE);
01030           str = STR_SMALLMAP_INDUSTRY;
01031         } else {
01032           if (tbl->col_break) {
01033             this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
01034             height = 0;
01035             num_columns++;
01036           }
01037           height++;
01038           str = tbl->legend;
01039         }
01040         min_width = max(GetStringBoundingBox(str).width, min_width);
01041       }
01042       this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
01043       this->min_number_of_columns = max(this->min_number_of_columns, num_columns);
01044     }
01045 
01046     /* The width of a column is the minimum width of all texts + the size of the blob + some spacing */
01047     this->column_width = min_width + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01048   }
01049 
01050   virtual void DrawWidget(const Rect &r, int widget) const
01051   {
01052     switch (widget) {
01053       case SM_WIDGET_MAP: {
01054         DrawPixelInfo new_dpi;
01055         if (!FillDrawPixelInfo(&new_dpi, r.left + 1, r.top + 1, r.right - r.left - 1, r.bottom - r.top - 1)) return;
01056         this->DrawSmallMap(&new_dpi);
01057       } break;
01058 
01059       case SM_WIDGET_LEGEND: {
01060         uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
01061         uint number_of_rows = max(this->map_type == SMT_INDUSTRY ? (_smallmap_industry_count + columns - 1) / columns : 0, this->min_number_of_fixed_rows);
01062         bool rtl = _dynlang.text_dir == TD_RTL;
01063         uint y_org = r.top + WD_FRAMERECT_TOP;
01064         uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
01065         uint y = y_org;
01066         uint i = 0; // Row counter for industry legend.
01067         uint row_height = FONT_HEIGHT_SMALL;
01068 
01069         uint text_left  = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
01070         uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
01071         uint blob_left  = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
01072         uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
01073 
01074         for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
01075           if (tbl->col_break || (this->map_type == SMT_INDUSTRY && i++ >= number_of_rows)) {
01076             /* Column break needed, continue at top, COLUMN_WIDTH pixels
01077              * (one "row") to the right. */
01078             x += rtl ? -(int)this->column_width : this->column_width;
01079             y = y_org;
01080             i = 1;
01081           }
01082 
01083           if (this->map_type == SMT_INDUSTRY) {
01084             /* Industry name must be formatted, since it's not in tiny font in the specs.
01085              * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font */
01086             SetDParam(0, tbl->legend);
01087             assert(tbl->type < NUM_INDUSTRYTYPES);
01088             SetDParam(1, _industry_counts[tbl->type]);
01089             if (!tbl->show_on_map) {
01090               /* Simply draw the string, not the black border of the legend colour.
01091                * This will enforce the idea of the disabled item */
01092               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
01093             } else {
01094               DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
01095               GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0); // Outer border of the legend colour
01096             }
01097           } else {
01098             /* Anything that is not an industry is using normal process */
01099             GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
01100             DrawString(x + text_left, x + text_right, y, tbl->legend);
01101           }
01102           GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour); // Legend colour
01103 
01104           y += row_height;
01105         }
01106       }
01107     }
01108   }
01109 
01110   virtual void OnPaint()
01111   {
01112     this->DrawWidgets();
01113   }
01114 
01115   virtual void OnClick(Point pt, int widget, int click_count)
01116   {
01117     switch (widget) {
01118       case SM_WIDGET_MAP: { // Map window
01119         /*
01120          * XXX: scrolling with the left mouse button is done by subsequently
01121          * clicking with the left mouse button; clicking once centers the
01122          * large map at the selected point. So by unclicking the left mouse
01123          * button here, it gets reclicked during the next inputloop, which
01124          * would make it look like the mouse is being dragged, while it is
01125          * actually being (virtually) clicked every inputloop.
01126          */
01127         _left_button_clicked = false;
01128 
01129         const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01130         Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01131         int sub;
01132         pt = this->PixelToTile(pt.x - wid->pos_x, pt.y - wid->pos_y, &sub);
01133         pt = RemapCoords(this->scroll_x + pt.x * TILE_SIZE + this->zoom * (TILE_SIZE - sub * TILE_SIZE / 4),
01134             this->scroll_y + pt.y * TILE_SIZE + sub * this->zoom * TILE_SIZE / 4, 0);
01135 
01136         w->viewport->follow_vehicle = INVALID_VEHICLE;
01137         w->viewport->dest_scrollpos_x = pt.x - (w->viewport->virtual_width  >> 1);
01138         w->viewport->dest_scrollpos_y = pt.y - (w->viewport->virtual_height >> 1);
01139 
01140         this->SetDirty();
01141       } break;
01142 
01143       case SM_WIDGET_ZOOM_IN:
01144       case SM_WIDGET_ZOOM_OUT: {
01145         const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01146         Point pt = {wid->current_x / 2, wid->current_y / 2};
01147         this->SetZoomLevel((widget == SM_WIDGET_ZOOM_IN) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
01148         SndPlayFx(SND_15_BEEP);
01149         break;
01150       }
01151 
01152       case SM_WIDGET_CONTOUR:    // Show land contours
01153       case SM_WIDGET_VEHICLES:   // Show vehicles
01154       case SM_WIDGET_INDUSTRIES: // Show industries
01155       case SM_WIDGET_ROUTES:     // Show transport routes
01156       case SM_WIDGET_VEGETATION: // Show vegetation
01157       case SM_WIDGET_OWNERS:     // Show land owners
01158         this->RaiseWidget(this->map_type + SM_WIDGET_CONTOUR);
01159         this->map_type = (SmallMapType)(widget - SM_WIDGET_CONTOUR);
01160         this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
01161 
01162         /* Hide Enable all/Disable all buttons if is not industry type small map */
01163         this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
01164 
01165         this->SetDirty();
01166         SndPlayFx(SND_15_BEEP);
01167         break;
01168 
01169       case SM_WIDGET_CENTERMAP: // Center the smallmap again
01170         this->SmallMapCenterOnCurrentPos();
01171         this->HandleButtonClick(SM_WIDGET_CENTERMAP);
01172         SndPlayFx(SND_15_BEEP);
01173         break;
01174 
01175       case SM_WIDGET_TOGGLETOWNNAME: // Toggle town names
01176         this->show_towns = !this->show_towns;
01177         this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
01178 
01179         this->SetDirty();
01180         SndPlayFx(SND_15_BEEP);
01181         break;
01182 
01183       case SM_WIDGET_LEGEND: // Legend
01184         /* If industry type small map*/
01185         if (this->map_type == SMT_INDUSTRY) {
01186           /* If click on industries label, find right industry type and enable/disable it */
01187           const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND); // Label panel
01188           uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
01189           uint columns = this->GetNumberColumnsLegend(wi->current_x);
01190           uint number_of_rows = max((_smallmap_industry_count + columns - 1) / columns, this->min_number_of_fixed_rows);
01191           if (line >= number_of_rows) break;
01192 
01193           bool rtl = _dynlang.text_dir == TD_RTL;
01194           int x = pt.x - wi->pos_x;
01195           if (rtl) x = wi->current_x - x;
01196           uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
01197 
01198           /* Check if click is on industry label*/
01199           int industry_pos = (column * number_of_rows) + line;
01200           if (industry_pos < _smallmap_industry_count) {
01201             _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
01202           }
01203 
01204           /* Raise the two buttons "all", as we have done a specific choice */
01205           this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
01206           this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
01207           this->SetDirty();
01208         }
01209         break;
01210 
01211       case SM_WIDGET_ENABLEINDUSTRIES: // Enable all industries
01212         for (int i = 0; i != _smallmap_industry_count; i++) {
01213           _legend_from_industries[i].show_on_map = true;
01214         }
01215         /* Toggle appeareance indicating the choice */
01216         this->LowerWidget(SM_WIDGET_ENABLEINDUSTRIES);
01217         this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
01218         this->SetDirty();
01219         break;
01220 
01221       case SM_WIDGET_DISABLEINDUSTRIES: // Disable all industries
01222         for (int i = 0; i != _smallmap_industry_count; i++) {
01223           _legend_from_industries[i].show_on_map = false;
01224         }
01225         /* Toggle appeareance indicating the choice */
01226         this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
01227         this->LowerWidget(SM_WIDGET_DISABLEINDUSTRIES);
01228         this->SetDirty();
01229         break;
01230 
01231       case SM_WIDGET_SHOW_HEIGHT: // Enable/disable showing of heightmap.
01232         _smallmap_industry_show_heightmap = !_smallmap_industry_show_heightmap;
01233         this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
01234         this->SetDirty();
01235         break;
01236     }
01237   }
01238 
01239   virtual void OnRightClick(Point pt, int widget)
01240   {
01241     if (widget == SM_WIDGET_MAP) {
01242       if (_scrolling_viewport) return;
01243       _scrolling_viewport = true;
01244     }
01245   }
01246 
01247   virtual void OnMouseWheel(int wheel)
01248   {
01249     const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01250     int cursor_x = _cursor.pos.x - this->left - wid->pos_x;
01251     int cursor_y = _cursor.pos.y - this->top  - wid->pos_y;
01252     if (IsInsideMM(cursor_x, 0, wid->current_x) && IsInsideMM(cursor_y, 0, wid->current_y)) {
01253       Point pt = {cursor_x, cursor_y};
01254       this->SetZoomLevel((wheel < 0) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
01255     }
01256   }
01257 
01258   virtual void OnTick()
01259   {
01260     /* Update the window every now and then */
01261     if (--this->refresh != 0) return;
01262 
01263     this->refresh = FORCE_REFRESH_PERIOD;
01264     this->SetDirty();
01265   }
01266 
01274   void SetNewScroll(int sx, int sy, int sub)
01275   {
01276     const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01277     Point hv = InverseRemapCoords(wi->current_x * TILE_SIZE / 2, wi->current_y * TILE_SIZE / 2);
01278     hv.x *= this->zoom;
01279     hv.y *= this->zoom;
01280 
01281     if (sx < -hv.x) {
01282       sx = -hv.x;
01283       sub = 0;
01284     }
01285     if (sx > (int)MapMaxX() * TILE_SIZE - hv.x) {
01286       sx = MapMaxX() * TILE_SIZE - hv.x;
01287       sub = 0;
01288     }
01289     if (sy < -hv.y) {
01290       sy = -hv.y;
01291       sub = 0;
01292     }
01293     if (sy > (int)MapMaxY() * TILE_SIZE - hv.y) {
01294       sy = MapMaxY() * TILE_SIZE - hv.y;
01295       sub = 0;
01296     }
01297 
01298     this->scroll_x = sx;
01299     this->scroll_y = sy;
01300     this->subscroll = sub;
01301   }
01302 
01303   virtual void OnScroll(Point delta)
01304   {
01305     _cursor.fix_at = true;
01306 
01307     /* While tile is at (delta.x, delta.y)? */
01308     int sub;
01309     Point pt = this->PixelToTile(delta.x, delta.y, &sub);
01310     this->SetNewScroll(this->scroll_x + pt.x * TILE_SIZE, this->scroll_y + pt.y * TILE_SIZE, sub);
01311 
01312     this->SetDirty();
01313   }
01314 
01315   void SmallMapCenterOnCurrentPos()
01316   {
01317     const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
01318     Point pt = InverseRemapCoords(vp->virtual_left + vp->virtual_width  / 2, vp->virtual_top  + vp->virtual_height / 2);
01319 
01320     int sub;
01321     const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01322     Point sxy = this->ComputeScroll(pt.x / TILE_SIZE, pt.y / TILE_SIZE, max(0, (int)wid->current_x / 2 - 2), wid->current_y / 2, &sub);
01323     this->SetNewScroll(sxy.x, sxy.y, sub);
01324     this->SetDirty();
01325   }
01326 };
01327 
01328 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
01329 bool SmallMapWindow::show_towns = true;
01330 
01339 class NWidgetSmallmapDisplay : public NWidgetContainer {
01340   const SmallMapWindow *smallmap_window; 
01341 public:
01342   NWidgetSmallmapDisplay() : NWidgetContainer(NWID_VERTICAL)
01343   {
01344     this->smallmap_window = NULL;
01345   }
01346 
01347   virtual void SetupSmallestSize(Window *w, bool init_array)
01348   {
01349     NWidgetBase *display = this->head;
01350     NWidgetBase *bar = display->next;
01351 
01352     display->SetupSmallestSize(w, init_array);
01353     bar->SetupSmallestSize(w, init_array);
01354 
01355     this->smallmap_window = dynamic_cast<SmallMapWindow *>(w);
01356     this->smallest_x = max(display->smallest_x, bar->smallest_x + smallmap_window->GetMinLegendWidth());
01357     this->smallest_y = display->smallest_y + max(bar->smallest_y, smallmap_window->GetMaxLegendHeight());
01358     this->fill_x = max(display->fill_x, bar->fill_x);
01359     this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : min(display->fill_y, bar->fill_y);
01360     this->resize_x = max(display->resize_x, bar->resize_x);
01361     this->resize_y = min(display->resize_y, bar->resize_y);
01362   }
01363 
01364   virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
01365   {
01366     this->pos_x = x;
01367     this->pos_y = y;
01368     this->current_x = given_width;
01369     this->current_y = given_height;
01370 
01371     NWidgetBase *display = this->head;
01372     NWidgetBase *bar = display->next;
01373 
01374     if (sizing == ST_SMALLEST) {
01375       this->smallest_x = given_width;
01376       this->smallest_y = given_height;
01377       /* Make display and bar exactly equal to their minimal size. */
01378       display->AssignSizePosition(ST_SMALLEST, x, y, display->smallest_x, display->smallest_y, rtl);
01379       bar->AssignSizePosition(ST_SMALLEST, x, y + display->smallest_y, bar->smallest_x, bar->smallest_y, rtl);
01380     }
01381 
01382     uint bar_height = max(bar->smallest_y, this->smallmap_window->GetLegendHeight(given_width - bar->smallest_x));
01383     uint display_height = given_height - bar_height;
01384     display->AssignSizePosition(ST_RESIZE, x, y, given_width, display_height, rtl);
01385     bar->AssignSizePosition(ST_RESIZE, x, y + display_height, given_width, bar_height, rtl);
01386   }
01387 
01388   virtual NWidgetCore *GetWidgetFromPos(int x, int y)
01389   {
01390     if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
01391     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01392       NWidgetCore *widget = child_wid->GetWidgetFromPos(x, y);
01393       if (widget != NULL) return widget;
01394     }
01395     return NULL;
01396   }
01397 
01398   virtual void Draw(const Window *w)
01399   {
01400     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) child_wid->Draw(w);
01401   }
01402 };
01403 
01405 static const NWidgetPart _nested_smallmap_display[] = {
01406   NWidget(WWT_PANEL, COLOUR_BROWN, SM_WIDGET_MAP_BORDER),
01407     NWidget(WWT_INSET, COLOUR_BROWN, SM_WIDGET_MAP), SetMinimalSize(346, 140), SetResize(1, 1), SetPadding(2, 2, 2, 2), EndContainer(),
01408   EndContainer(),
01409 };
01410 
01412 static const NWidgetPart _nested_smallmap_bar[] = {
01413   NWidget(WWT_PANEL, COLOUR_BROWN),
01414     NWidget(NWID_HORIZONTAL),
01415       NWidget(WWT_EMPTY, INVALID_COLOUR, SM_WIDGET_LEGEND), SetResize(1, 1),
01416       NWidget(NWID_VERTICAL),
01417         /* Top button row. */
01418         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01419           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_IN), SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN),
01420           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP), SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER),
01421           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR), SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP),
01422           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES), SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP),
01423           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES), SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP),
01424         EndContainer(),
01425         /* Bottom button row. */
01426         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01427           NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_OUT), SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT),
01428           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME), SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF),
01429           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES), SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON),
01430           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION), SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP),
01431           NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS), SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP),
01432         EndContainer(),
01433         NWidget(NWID_SPACER), SetResize(0, 1),
01434       EndContainer(),
01435     EndContainer(),
01436   EndContainer(),
01437 };
01438 
01439 static NWidgetBase *SmallMapDisplay(int *biggest_index)
01440 {
01441   NWidgetContainer *map_display = new NWidgetSmallmapDisplay;
01442 
01443   MakeNWidgets(_nested_smallmap_display, lengthof(_nested_smallmap_display), biggest_index, map_display);
01444   MakeNWidgets(_nested_smallmap_bar, lengthof(_nested_smallmap_bar), biggest_index, map_display);
01445   return map_display;
01446 }
01447 
01448 
01449 static const NWidgetPart _nested_smallmap_widgets[] = {
01450   NWidget(NWID_HORIZONTAL),
01451     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
01452     NWidget(WWT_CAPTION, COLOUR_BROWN, SM_WIDGET_CAPTION), SetDataTip(STR_SMALLMAP_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01453     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
01454     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
01455   EndContainer(),
01456   NWidgetFunction(SmallMapDisplay), // Smallmap display and legend bar + image buttons.
01457   /* Bottom button row and resize box. */
01458   NWidget(NWID_HORIZONTAL),
01459     NWidget(WWT_PANEL, COLOUR_BROWN),
01460       NWidget(NWID_HORIZONTAL),
01461         NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECTINDUSTRIES),
01462           NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01463             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_SMALLMAP_TOOLTIP_ENABLE_ALL),
01464             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_SMALLMAP_TOOLTIP_DISABLE_ALL),
01465             NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
01466           EndContainer(),
01467           NWidget(NWID_SPACER), SetFill(1, 1),
01468         EndContainer(),
01469         NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
01470       EndContainer(),
01471     EndContainer(),
01472     NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
01473   EndContainer(),
01474 };
01475 
01476 static const WindowDesc _smallmap_desc(
01477   WDP_AUTO, 446, 314,
01478   WC_SMALLMAP, WC_NONE,
01479   WDF_UNCLICK_BUTTONS,
01480   _nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
01481 );
01482 
01483 void ShowSmallMap()
01484 {
01485   AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
01486 }
01487 
01496 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
01497 {
01498   bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
01499 
01500   /* If a user scrolls to a tile (via what way what so ever) and already is on
01501    * that tile (e.g.: pressed twice), move the smallmap to that location,
01502    * so you directly see where you are on the smallmap. */
01503 
01504   if (res) return res;
01505 
01506   SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
01507   if (w != NULL) w->SmallMapCenterOnCurrentPos();
01508 
01509   return res;
01510 }

Generated on Wed Mar 3 23:32:26 2010 for OpenTTD by  doxygen 1.6.1