# smallmap_gui.cpp

Go to the documentation of this file.

    00001
    00002 /* $Id: smallmap_gui.cpp 14269 2008-09-07 22:14:48Z rubidium $ */
    00003
    00006 #include "stdafx.h"
    00007 #include "openttd.h"
    00008 #include "bridge_map.h"
    00009 #include "clear_map.h"
    00010 #include "industry_map.h"
    00011 #include "industry.h"
    00012 #include "station_map.h"
    00013 #include "landscape.h"
    00014 #include "gui.h"
    00015 #include "window_gui.h"
    00016 #include "tree_map.h"
    00017 #include "tunnel_map.h"
    00018 #include "viewport_func.h"
    00019 #include "gfx_func.h"
    00020 #include "player_base.h"
    00021 #include "town.h"
    00022 #include "variables.h"
    00023 #include "blitter/factory.hpp"
    00024 #include "tunnelbridge_map.h"
    00025 #include "strings_func.h"
    00026 #include "zoom_func.h"
    00027 #include "core/endian_func.hpp"
    00028 #include "vehicle_base.h"
    00029 #include "sound_func.h"
    00030 #include "settings_type.h"
    00031
    00032 #include "table/strings.h"
    00033 #include "table/sprites.h"
    00034
    00035 static const Widget _smallmap_widgets[] = {
    00036 {  WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},
    00037 {   WWT_CAPTION,  RESIZE_RIGHT,    13,    11,   337,     0,    13, STR_00B0_MAP,            STR_018C_WINDOW_TITLE_DRAG_THIS},
    00038 { WWT_STICKYBOX,     RESIZE_LR,    13,   338,   349,     0,    13, 0x0,                     STR_STICKY_BUTTON},
    00039 {     WWT_PANEL,     RESIZE_RB,    13,     0,   349,    14,   157, 0x0,                     STR_NULL},
    00040 {     WWT_INSET,     RESIZE_RB,    13,     2,   347,    16,   155, 0x0,                     STR_NULL},
    00041 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   284,   305,   158,   179, SPR_IMG_SHOW_COUNTOURS,  STR_0191_SHOW_LAND_CONTOURS_ON_MAP},
    00042 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   306,   327,   158,   179, SPR_IMG_SHOW_VEHICLES,   STR_0192_SHOW_VEHICLES_ON_MAP},
    00043 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   328,   349,   158,   179, SPR_IMG_INDUSTRY,        STR_0193_SHOW_INDUSTRIES_ON_MAP},
    00044 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   284,   307,   180,   201, SPR_IMG_SHOW_ROUTES,     STR_0194_SHOW_TRANSPORT_ROUTES_ON},
    00045 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   306,   327,   180,   201, SPR_IMG_PLANTTREES,      STR_0195_SHOW_VEGETATION_ON_MAP},
    00046 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   328,   349,   180,   201, SPR_IMG_COMPANY_GENERAL, STR_0196_SHOW_LAND_OWNERS_ON_MAP},
    00047 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   262,   283,   158,   179, SPR_IMG_SMALLMAP,        STR_SMALLMAP_CENTER},
    00048 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   262,   283,   180,   201, SPR_IMG_TOWN,            STR_0197_TOGGLE_TOWN_NAMES_ON_OFF},
    00049 {     WWT_PANEL,    RESIZE_RTB,    13,     0,   261,   158,   201, 0x0,                     STR_NULL},
    00050 {     WWT_PANEL,   RESIZE_LRTB,    13,   262,   349,   202,   202, 0x0,                     STR_NULL},
    00051 {     WWT_PANEL,    RESIZE_RTB,    13,     0,   337,   202,   213, 0x0,                     STR_NULL},
    00052 {   WWT_TEXTBTN,     RESIZE_TB,    13,     0,    99,   202,   213, STR_MESSAGES_ENABLE_ALL, STR_NULL},
    00053 {   WWT_TEXTBTN,     RESIZE_TB,    13,   100,   201,   202,   213, STR_MESSAGES_DISABLE_ALL,STR_NULL},
    00054 { WWT_RESIZEBOX,   RESIZE_LRTB,    13,   338,   349,   202,   213, 0x0,                     STR_RESIZE_BUTTON},
    00055 {  WIDGETS_END},
    00056 };
    00057
    00058 static int _smallmap_type;
    00059 static bool _smallmap_show_towns = true;
    00060 /* number of used industries */
    00061 static int _smallmap_industry_count;
    00062 /* number of industries per column*/
    00063 static uint _industries_per_column;
    00064
    00066 #define MK(a,b) {a, b, INVALID_INDUSTRYTYPE, true, false, false}
    00067
    00068 #define MKEND() {0, STR_NULL, INVALID_INDUSTRYTYPE, true, true, false}
    00069
    00071 #define MS(a,b) {a, b, INVALID_INDUSTRYTYPE, true, false, true}
    00072
    00074 struct LegendAndColour {
    00075   uint16 colour;
    00076   StringID legend;
    00077   IndustryType type;
    00078   bool show_on_map;
    00079   bool end;
    00080   bool col_break;
    00081 };
    00082
    00084 static const LegendAndColour _legend_land_contours[] = {
    00085   MK(0x5A, STR_00F0_100M),
    00086   MK(0x5C, STR_00F1_200M),
    00087   MK(0x5E, STR_00F2_300M),
    00088   MK(0x1F, STR_00F3_400M),
    00089   MK(0x27, STR_00F4_500M),
    00090
    00091   MS(0xD7, STR_00EB_ROADS),
    00092   MK(0x0A, STR_00EC_RAILROADS),
    00093   MK(0x98, STR_00ED_STATIONS_AIRPORTS_DOCKS),
    00094   MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
    00095   MK(0x0F, STR_00EF_VEHICLES),
    00096   MKEND()
    00097 };
    00098
    00099 static const LegendAndColour _legend_vehicles[] = {
    00100   MK(0xB8, STR_00F5_TRAINS),
    00101   MK(0xBF, STR_00F6_ROAD_VEHICLES),
    00102   MK(0x98, STR_00F7_SHIPS),
    00103   MK(0x0F, STR_00F8_AIRCRAFT),
    00104   MS(0xD7, STR_00F9_TRANSPORT_ROUTES),
    00105   MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
    00106   MKEND()
    00107 };
    00108
    00109 static const LegendAndColour _legend_routes[] = {
    00110   MK(0xD7, STR_00EB_ROADS),
    00111   MK(0x0A, STR_00EC_RAILROADS),
    00112   MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
    00113   MS(0x56, STR_011B_RAILROAD_STATION),
    00114
    00115   MK(0xC2, STR_011C_TRUCK_LOADING_BAY),
    00116   MK(0xBF, STR_011D_BUS_STATION),
    00117   MK(0xB8, STR_011E_AIRPORT_HELIPORT),
    00118   MK(0x98, STR_011F_DOCK),
    00119   MKEND()
    00120 };
    00121
    00122 static const LegendAndColour _legend_vegetation[] = {
    00123   MK(0x52, STR_0120_ROUGH_LAND),
    00124   MK(0x54, STR_0121_GRASS_LAND),
    00125   MK(0x37, STR_0122_BARE_LAND),
    00126   MK(0x25, STR_0123_FIELDS),
    00127   MK(0x57, STR_0124_TREES),
    00128   MK(0xD0, STR_00FC_FOREST),
    00129   MS(0x0A, STR_0125_ROCKS),
    00130
    00131   MK(0xC2, STR_012A_DESERT),
    00132   MK(0x98, STR_012B_SNOW),
    00133   MK(0xD7, STR_00F9_TRANSPORT_ROUTES),
    00134   MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
    00135   MKEND()
    00136 };
    00137
    00138 static const LegendAndColour _legend_land_owners[] = {
    00139   MK(0xCA, STR_0126_WATER),
    00140   MK(0x54, STR_0127_NO_OWNER),
    00141   MK(0xB4, STR_0128_TOWNS),
    00142   MK(0x20, STR_0129_INDUSTRIES),
    00143   MKEND()
    00144 };
    00145 #undef MK
    00146 #undef MS
    00147 #undef MKEND
    00148
    00151 static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES+1];
    00152 /* For connecting industry type to position in industries list(small map legend) */
    00153 static uint _industry_to_list_pos[NUM_INDUSTRYTYPES];
    00154
    00158 void BuildIndustriesLegend()
    00159 {
    00160   const IndustrySpec *indsp;
    00161   uint j = 0;
    00162   uint free_slot, diff;
    00163
    00164   /* Add each name */
    00165   for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
    00166     indsp = GetIndustrySpec(i);
    00167     if (indsp->enabled) {
    00168       _legend_from_industries[j].legend = indsp->name;
    00169       _legend_from_industries[j].colour = indsp->map_colour;
    00170       _legend_from_industries[j].type = i;
    00171       _legend_from_industries[j].show_on_map = true;
    00172       _legend_from_industries[j].col_break = false;
    00173       _legend_from_industries[j].end = false;
    00174
    00175       /* Store widget number for this industry type */
    00176       _industry_to_list_pos[i] = j;
    00177       j++;
    00178     }
    00179   }
    00180   /* Terminate the list */
    00181   _legend_from_industries[j].end = true;
    00182
    00183   /* Store number of enabled industries */
    00184   _smallmap_industry_count = j;
    00185
    00186   _industries_per_column = _smallmap_industry_count / 3;
    00187   free_slot = _smallmap_industry_count % 3;
    00188
    00189   /* recalculate column break for first two columns(i) */
    00190   diff = 0;
    00191   for (int i = 1; i <= 2; i++) {
    00192     if (free_slot > 0) diff = diff + 1;
    00193     _legend_from_industries[i * _industries_per_column + diff].col_break = true;
    00194     if (free_slot > 0) free_slot--;
    00195   }
    00196
    00197 }
    00198
    00199 static const LegendAndColour * const _legend_table[] = {
    00200   _legend_land_contours,
    00201   _legend_vehicles,
    00202   _legend_from_industries,
    00203   _legend_routes,
    00204   _legend_vegetation,
    00205   _legend_land_owners,
    00206 };
    00207
    00208 #define MKCOLOR(x) TO_LE32X(x)
    00209
    00213 static const uint32 _map_height_bits[] = {
    00214   MKCOLOR(0x5A5A5A5A),
    00215   MKCOLOR(0x5A5B5A5B),
    00216   MKCOLOR(0x5B5B5B5B),
    00217   MKCOLOR(0x5B5C5B5C),
    00218   MKCOLOR(0x5C5C5C5C),
    00219   MKCOLOR(0x5C5D5C5D),
    00220   MKCOLOR(0x5D5D5D5D),
    00221   MKCOLOR(0x5D5E5D5E),
    00222   MKCOLOR(0x5E5E5E5E),
    00223   MKCOLOR(0x5E5F5E5F),
    00224   MKCOLOR(0x5F5F5F5F),
    00225   MKCOLOR(0x5F1F5F1F),
    00226   MKCOLOR(0x1F1F1F1F),
    00227   MKCOLOR(0x1F271F27),
    00228   MKCOLOR(0x27272727),
    00229   MKCOLOR(0x27272727),
    00230 };
    00231 assert_compile(lengthof(_map_height_bits) == MAX_TILE_HEIGHT + 1);
    00232
    00233 struct AndOr {
    00234   uint32 mor;
    00235   uint32 mand;
    00236 };
    00237
    00238 static inline uint32 ApplyMask(uint32 colour, const AndOr *mask)
    00239 {
    00240   return (colour & mask->mand) | mask->mor;
    00241 }
    00242
    00243
    00244 static const AndOr _smallmap_contours_andor[] = {
    00245   {MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
    00246   {MKCOLOR(0x000A0A00), MKCOLOR(0xFF0000FF)},
    00247   {MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
    00248   {MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
    00249   {MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
    00250   {MKCOLOR(0x98989898), MKCOLOR(0x00000000)},
    00251   {MKCOLOR(0xCACACACA), MKCOLOR(0x00000000)},
    00252   {MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
    00253   {MKCOLOR(0xB5B5B5B5), MKCOLOR(0x00000000)},
    00254   {MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
    00255   {MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
    00256   {MKCOLOR(0x000A0A00), MKCOLOR(0xFF0000FF)},
    00257 };
    00258
    00259 static const AndOr _smallmap_vehicles_andor[] = {
    00260   {MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
    00261   {MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
    00262   {MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
    00263   {MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
    00264   {MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
    00265   {MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
    00266   {MKCOLOR(0xCACACACA), MKCOLOR(0x00000000)},
    00267   {MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
    00268   {MKCOLOR(0xB5B5B5B5), MKCOLOR(0x00000000)},
    00269   {MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
    00270   {MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
    00271   {MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
    00272 };
    00273
    00274 static const AndOr _smallmap_vegetation_andor[] = {
    00275   {MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
    00276   {MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
    00277   {MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
    00278   {MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
    00279   {MKCOLOR(0x00575700), MKCOLOR(0xFF0000FF)},
    00280   {MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
    00281   {MKCOLOR(0xCACACACA), MKCOLOR(0x00000000)},
    00282   {MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
    00283   {MKCOLOR(0xB5B5B5B5), MKCOLOR(0x00000000)},
    00284   {MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
    00285   {MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
    00286   {MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
    00287 };
    00288
    00289 typedef uint32 GetSmallMapPixels(TileIndex tile); // typedef callthrough function
    00290
    00304 static void DrawSmallMapStuff(void *dst, uint xc, uint yc, int pitch, int reps, uint32 mask, GetSmallMapPixels *proc)
    00305 {
    00306   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00307   void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
    00308   void *dst_ptr_end = blitter->MoveTo(dst_ptr_abs_end, -4, 0);
    00309
    00310   do {
    00311     /* check if the tile (xc,yc) is within the map range */
    00312     if (xc < MapMaxX() && yc < MapMaxY()) {
    00313       /* check if the dst pointer points to a pixel inside the screen buffer */
    00314       if (dst < _screen.dst_ptr) continue;
    00315       if (dst >= dst_ptr_abs_end) continue;
    00316
    00317       uint32 val = proc(TileXY(xc, yc)) & mask;
    00318       uint8 *val8 = (uint8 *)&val;
    00319
    00320       if (dst <= dst_ptr_end) {
    00321         blitter->SetPixelIfEmpty(dst, 0, 0, val8[0]);
    00322         blitter->SetPixelIfEmpty(dst, 1, 0, val8[1]);
    00323         blitter->SetPixelIfEmpty(dst, 2, 0, val8[2]);
    00324         blitter->SetPixelIfEmpty(dst, 3, 0, val8[3]);
    00325       } else {
    00326         /* It happens that there are only 1, 2 or 3 pixels left to fill, so in that special case, write till the end of the video-buffer */
    00327         int i = 0;
    00328         do {
    00329           blitter->SetPixelIfEmpty(dst, 0, 0, val8[i]);
    00330         } while (i++, dst = blitter->MoveTo(dst, 1, 0), dst < dst_ptr_abs_end);
    00331       }
    00332     }
    00333   /* switch to next tile in the column */
    00334   } while (xc++, yc++, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
    00335 }
    00336
    00337
    00338 static inline TileType GetEffectiveTileType(TileIndex tile)
    00339 {
    00340   TileType t = GetTileType(tile);
    00341
    00342   if (t == MP_TUNNELBRIDGE) {
    00343     TransportType tt = GetTunnelBridgeTransportType(tile);
    00344
    00345     switch (tt) {
    00346       case TRANSPORT_RAIL: t = MP_RAILWAY; break;
    00347       case TRANSPORT_ROAD: t = MP_ROAD;    break;
    00348       default:             t = MP_WATER;   break;
    00349     }
    00350   }
    00351   return t;
    00352 }
    00353
    00359 static inline uint32 GetSmallMapContoursPixels(TileIndex tile)
    00360 {
    00361   TileType t = GetEffectiveTileType(tile);
    00362
    00363   return
    00364     ApplyMask(_map_height_bits[TileHeight(tile)], &_smallmap_contours_andor[t]);
    00365 }
    00366
    00373 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile)
    00374 {
    00375   TileType t = GetEffectiveTileType(tile);
    00376
    00377   return ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
    00378 }
    00379
    00386 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile)
    00387 {
    00388   TileType t = GetEffectiveTileType(tile);
    00389
    00390   if (t == MP_INDUSTRY) {
    00391     /* If industry is allowed to be seen, use its color on the map */
    00392     if (_legend_from_industries[_industry_to_list_pos[GetIndustryByTile(tile)->type]].show_on_map) {
    00393       return GetIndustrySpec(GetIndustryByTile(tile)->type)->map_colour * 0x01010101;
    00394     } else {
    00395       /* otherwise, return the color of the clear tiles, which will make it disappear */
    00396       return ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[MP_CLEAR]);
    00397     }
    00398   }
    00399
    00400   return ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
    00401 }
    00402
    00409 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile)
    00410 {
    00411   TileType t = GetEffectiveTileType(tile);
    00412   uint32 bits;
    00413
    00414   if (t == MP_STATION) {
    00415     switch (GetStationType(tile)) {
    00416       case STATION_RAIL:    bits = MKCOLOR(0x56565656); break;
    00417       case STATION_AIRPORT: bits = MKCOLOR(0xB8B8B8B8); break;
    00418       case STATION_TRUCK:   bits = MKCOLOR(0xC2C2C2C2); break;
    00419       case STATION_BUS:     bits = MKCOLOR(0xBFBFBFBF); break;
    00420       case STATION_DOCK:    bits = MKCOLOR(0x98989898); break;
    00421       default:              bits = MKCOLOR(0xFFFFFFFF); break;
    00422     }
    00423   } else {
    00424     /* ground color */
    00425     bits = ApplyMask(MKCOLOR(0x54545454), &_smallmap_contours_andor[t]);
    00426   }
    00427   return bits;
    00428 }
    00429
    00430
    00431 static const uint32 _vegetation_clear_bits[] = {
    00432   MKCOLOR(0x54545454),
    00433   MKCOLOR(0x52525252),
    00434   MKCOLOR(0x0A0A0A0A),
    00435   MKCOLOR(0x25252525),
    00436   MKCOLOR(0x98989898),
    00437   MKCOLOR(0xC2C2C2C2),
    00438   MKCOLOR(0x54545454),
    00439   MKCOLOR(0x54545454),
    00440 };
    00441
    00442 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile)
    00443 {
    00444   TileType t = GetEffectiveTileType(tile);
    00445   uint32 bits;
    00446
    00447   switch (t) {
    00448     case MP_CLEAR:
    00449       if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) {
    00450         bits = MKCOLOR(0x37373737);
    00451       } else {
    00452         bits = _vegetation_clear_bits[GetClearGround(tile)];
    00453       }
    00454       break;
    00455
    00456     case MP_INDUSTRY:
    00457       bits = GetIndustrySpec(GetIndustryByTile(tile)->type)->check_proc == CHECK_FOREST ? MKCOLOR(0xD0D0D0D0) : MKCOLOR(0xB5B5B5B5);
    00458       break;
    00459
    00460     case MP_TREES:
    00461       if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT) {
    00462         bits = (_opt.landscape == LT_ARCTIC) ? MKCOLOR(0x98575798) : MKCOLOR(0xC25757C2);
    00463       } else {
    00464         bits = MKCOLOR(0x54575754);
    00465       }
    00466       break;
    00467
    00468     default:
    00469       bits = ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
    00470       break;
    00471   }
    00472
    00473   return bits;
    00474 }
    00475
    00476
    00477 static uint32 _owner_colors[OWNER_END + 1];
    00478
    00485 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile)
    00486 {
    00487   Owner o;
    00488
    00489   switch (GetTileType(tile)) {
    00490     case MP_INDUSTRY: o = OWNER_END;          break;
    00491     case MP_HOUSE:    o = OWNER_TOWN;         break;
    00492     default:          o = GetTileOwner(tile); break;
    00493   }
    00494
    00495   return _owner_colors[o];
    00496 }
    00497
    00498
    00499 static const uint32 _smallmap_mask_left[3] = {
    00500   MKCOLOR(0xFF000000),
    00501   MKCOLOR(0xFFFF0000),
    00502   MKCOLOR(0xFFFFFF00),
    00503 };
    00504
    00505 static const uint32 _smallmap_mask_right[] = {
    00506   MKCOLOR(0x000000FF),
    00507   MKCOLOR(0x0000FFFF),
    00508   MKCOLOR(0x00FFFFFF),
    00509 };
    00510
    00511 /* each tile has 4 x pixels and 1 y pixel */
    00512
    00513 static GetSmallMapPixels *_smallmap_draw_procs[] = {
    00514   GetSmallMapContoursPixels,
    00515   GetSmallMapVehiclesPixels,
    00516   GetSmallMapIndustriesPixels,
    00517   GetSmallMapRoutesPixels,
    00518   GetSmallMapVegetationPixels,
    00519   GetSmallMapOwnerPixels,
    00520 };
    00521
    00522 static const byte _vehicle_type_colors[6] = {
    00523   184, 191, 152, 15, 215, 184
    00524 };
    00525
    00526
    00527 static void DrawVertMapIndicator(int x, int y, int x2, int y2)
    00528 {
    00529   GfxFillRect(x, y,      x2, y + 3, 69);
    00530   GfxFillRect(x, y2 - 3, x2, y2,    69);
    00531 }
    00532
    00533 static void DrawHorizMapIndicator(int x, int y, int x2, int y2)
    00534 {
    00535   GfxFillRect(x,      y, x + 3, y2, 69);
    00536   GfxFillRect(x2 - 3, y, x2,    y2, 69);
    00537 }
    00538
    00539 enum SmallMapWindowWidgets {
    00540   SM_WIDGET_MAP = 4,
    00541   SM_WIDGET_CONTOUR,
    00542   SM_WIDGET_VEHICLES,
    00543   SM_WIDGET_INDUSTRIES,
    00544   SM_WIDGET_ROUTES,
    00545   SM_WIDGET_VEGETATION,
    00546   SM_WIDGET_OWNERS,
    00547   SM_WIDGET_CENTERMAP,
    00548   SM_WIDGET_TOGGLETOWNNAME,
    00549   SM_WIDGET_LEGEND,
    00550   SM_WIDGET_BUTTONSPANEL,
    00551   SM_WIDGET_BOTTOMPANEL,
    00552   SM_WIDGET_ENABLEINDUSTRIES,
    00553   SM_WIDGET_DISABLEINDUSTRIES,
    00554   SM_WIDGET_RESIZEBOX,
    00555 };
    00556
    00557 enum SmallMapType {
    00558   SMT_CONTOUR,
    00559   SMT_VEHICLES,
    00560   SMT_INDUSTRY,
    00561   SMT_OWNER = 5,
    00562 };
    00563
    00578 static void DrawSmallMap(DrawPixelInfo *dpi, Window *w, int type, bool show_towns)
    00579 {
    00580   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00581   DrawPixelInfo *old_dpi;
    00582   int dx,dy, x, y, x2, y2;
    00583   void *ptr;
    00584   int tile_x;
    00585   int tile_y;
    00586   ViewPort *vp;
    00587
    00588   old_dpi = _cur_dpi;
    00589   _cur_dpi = dpi;
    00590
    00591   /* clear it */
    00592   GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
    00593
    00594   /* setup owner table */
    00595   if (type == SMT_OWNER) {
    00596     const Player *p;
    00597
    00598     /* fill with some special colors */
    00599     _owner_colors[OWNER_TOWN] = MKCOLOR(0xB4B4B4B4);
    00600     _owner_colors[OWNER_NONE] = MKCOLOR(0x54545454);
    00601     _owner_colors[OWNER_WATER] = MKCOLOR(0xCACACACA);
    00602     _owner_colors[OWNER_END]   = MKCOLOR(0x20202020); /* industry */
    00603
    00604     /* now fill with the player colors */
    00605     FOR_ALL_PLAYERS(p) {
    00606       if (p->is_active) {
    00607         _owner_colors[p->index] =
    00608           _colour_gradient[p->player_color][5] * 0x01010101;
    00609       }
    00610     }
    00611   }
    00612
    00613   tile_x = WP(w, smallmap_d).scroll_x / TILE_SIZE;
    00614   tile_y = WP(w, smallmap_d).scroll_y / TILE_SIZE;
    00615
    00616   dx = dpi->left + WP(w, smallmap_d).subscroll;
    00617   tile_x -= dx / 4;
    00618   tile_y += dx / 4;
    00619   dx &= 3;
    00620
    00621   dy = dpi->top;
    00622   tile_x += dy / 2;
    00623   tile_y += dy / 2;
    00624
    00625   if (dy & 1) {
    00626     tile_x++;
    00627     dx += 2;
    00628     if (dx > 3) {
    00629       dx -= 4;
    00630       tile_x--;
    00631       tile_y++;
    00632     }
    00633   }
    00634
    00635   ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
    00636   x = - dx - 4;
    00637   y = 0;
    00638
    00639   for (;;) {
    00640     uint32 mask = 0xFFFFFFFF;
    00641     int reps;
    00642     int t;
    00643
    00644     /* distance from left edge */
    00645     if (x < 0) {
    00646       if (x < -3) goto skip_column;
    00647       /* mask to use at the left edge */
    00648       mask = _smallmap_mask_left[x + 3];
    00649     }
    00650
    00651     /* distance from right edge */
    00652     t = dpi->width - x;
    00653     if (t < 4) {
    00654       if (t <= 0) break; /* exit loop */
    00655       /* mask to use at the right edge */
    00656       mask &= _smallmap_mask_right[t - 1];
    00657     }
    00658
    00659     /* number of lines */
    00660     reps = (dpi->height - y + 1) / 2;
    00661     if (reps > 0) {
    00662       DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch * 2, reps, mask, _smallmap_draw_procs[type]);
    00663     }
    00664
    00665 skip_column:
    00666     if (y == 0) {
    00667       tile_y++;
    00668       y++;
    00669       ptr = blitter->MoveTo(ptr, 0, 1);
    00670     } else {
    00671       tile_x--;
    00672       y--;
    00673       ptr = blitter->MoveTo(ptr, 0, -1);
    00674     }
    00675     ptr = blitter->MoveTo(ptr, 2, 0);
    00676     x += 2;
    00677   }
    00678
    00679   /* draw vehicles? */
    00680   if (type == SMT_CONTOUR || type == SMT_VEHICLES) {
    00681     Vehicle *v;
    00682     bool skip;
    00683     byte color;
    00684
    00685     FOR_ALL_VEHICLES(v) {
    00686       if (v->type != VEH_SPECIAL &&
    00687           (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0) {
    00688         /* Remap into flat coordinates. */
    00689         Point pt = RemapCoords(
    00690           v->x_pos / TILE_SIZE - WP(w, smallmap_d).scroll_x / TILE_SIZE, // divide each one separately because (a-b)/c != a/c-b/c in integer world
    00691           v->y_pos / TILE_SIZE - WP(w, smallmap_d).scroll_y / TILE_SIZE, //    dtto
    00692           0);
    00693         x = pt.x;
    00694         y = pt.y;
    00695
    00696         /* Check if y is out of bounds? */
    00697         y -= dpi->top;
    00698         if (!IsInsideMM(y, 0, dpi->height)) continue;
    00699
    00700         /* Default is to draw both pixels. */
    00701         skip = false;
    00702
    00703         /* Offset X coordinate */
    00704         x -= WP(w, smallmap_d).subscroll + 3 + dpi->left;
    00705
    00706         if (x < 0) {
    00707           /* if x+1 is 0, that means we're on the very left edge,
    00708            *  and should thus only draw a single pixel */
    00709           if (++x != 0) continue;
    00710           skip = true;
    00711         } else if (x >= dpi->width - 1) {
    00712           /* Check if we're at the very right edge, and if so draw only a single pixel */
    00713           if (x != dpi->width - 1) continue;
    00714           skip = true;
    00715         }
    00716
    00717         /* Calculate pointer to pixel and the color */
    00718         color = (type == SMT_VEHICLES) ? _vehicle_type_colors[v->type] : 0xF;
    00719
    00720         /* And draw either one or two pixels depending on clipping */
    00721         blitter->SetPixel(dpi->dst_ptr, x, y, color);
    00722         if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, color);
    00723       }
    00724     }
    00725   }
    00726
    00727   if (show_towns) {
    00728     const Town *t;
    00729
    00730     FOR_ALL_TOWNS(t) {
    00731       /* Remap the town coordinate */
    00732       Point pt = RemapCoords(
    00733         (int)(TileX(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_x) / TILE_SIZE,
    00734         (int)(TileY(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_y) / TILE_SIZE,
    00735         0);
    00736       x = pt.x - WP(w, smallmap_d).subscroll + 3 - (t->sign.width_2 >> 1);
    00737       y = pt.y;
    00738
    00739       /* Check if the town sign is within bounds */
    00740       if (x + t->sign.width_2 > dpi->left &&
    00741           x < dpi->left + dpi->width &&
    00742           y + 6 > dpi->top &&
    00743           y < dpi->top + dpi->height) {
    00744         /* And draw it. */
    00745         SetDParam(0, t->index);
    00746         DrawString(x, y, STR_2056, TC_WHITE);
    00747       }
    00748     }
    00749   }
    00750
    00751   /* Draw map indicators */
    00752   {
    00753     Point pt;
    00754
    00755     /* Find main viewport. */
    00756     vp = FindWindowById(WC_MAIN_WINDOW,0)->viewport;
    00757
    00758     pt = RemapCoords(WP(w, smallmap_d).scroll_x, WP(w, smallmap_d).scroll_y, 0);
    00759
    00760     x = vp->virtual_left - pt.x;
    00761     y = vp->virtual_top - pt.y;
    00762     x2 = (x + vp->virtual_width) / TILE_SIZE;
    00763     y2 = (y + vp->virtual_height) / TILE_SIZE;
    00764     x /= TILE_SIZE;
    00765     y /= TILE_SIZE;
    00766
    00767     x -= WP(w, smallmap_d).subscroll;
    00768     x2 -= WP(w, smallmap_d).subscroll;
    00769
    00770     DrawVertMapIndicator(x, y, x, y2);
    00771     DrawVertMapIndicator(x2, y, x2, y2);
    00772
    00773     DrawHorizMapIndicator(x, y, x2, y);
    00774     DrawHorizMapIndicator(x, y2, x2, y2);
    00775   }
    00776   _cur_dpi = old_dpi;
    00777 }
    00778
    00779 void SmallMapCenterOnCurrentPos(Window *w)
    00780 {
    00781   int x, y;
    00782   ViewPort *vp;
    00783   vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
    00784
    00785   x  = ((vp->virtual_width  - (w->widget[SM_WIDGET_MAP].right  - w->widget[SM_WIDGET_MAP].left) * TILE_SIZE) / 2 + vp->virtual_left) / 4;
    00786   y  = ((vp->virtual_height - (w->widget[SM_WIDGET_MAP].bottom - w->widget[SM_WIDGET_MAP].top ) * TILE_SIZE) / 2 + vp->virtual_top ) / 2 - TILE_SIZE * 2;
    00787   WP(w, smallmap_d).scroll_x = (y - x) & ~0xF;
    00788   WP(w, smallmap_d).scroll_y = (x + y) & ~0xF;
    00789   SetWindowDirty(w);
    00790 }
    00791
    00792 enum {
    00793   BASE_NB_PER_COLUMN = 6,
    00794 };
    00795
    00796 static void SmallMapWindowProc(Window *w, WindowEvent *e)
    00797 {
    00798   switch (e->event) {
    00799     case WE_PAINT: {
    00800       const LegendAndColour *tbl;
    00801       int x, y, y_org;
    00802       uint diff;
    00803       DrawPixelInfo new_dpi;
    00804
    00805       /* Hide Enable all/Disable all buttons if is not industry type small map*/
    00806       w->SetWidgetHiddenState(SM_WIDGET_ENABLEINDUSTRIES, _smallmap_type != SMT_INDUSTRY);
    00807       w->SetWidgetHiddenState(SM_WIDGET_DISABLEINDUSTRIES, _smallmap_type != SMT_INDUSTRY);
    00808
    00809       /* draw the window */
    00810       SetDParam(0, STR_00E5_CONTOURS + _smallmap_type);
    00811       DrawWindowWidgets(w);
    00812
    00813       tbl = _legend_table[_smallmap_type];
    00814
    00815       /* difference in window size */
    00816       diff = (_industries_per_column > BASE_NB_PER_COLUMN) ? ((_industries_per_column - BASE_NB_PER_COLUMN) * BASE_NB_PER_COLUMN) + 1 : 0;
    00817
    00818       x = 4;
    00819       y_org = w->height - 44 - 11 - diff;
    00820       y = y_org;
    00821
    00822       for (;;) {
    00823
    00824         if (_smallmap_type == SMT_INDUSTRY) {
    00825           /* Industry name must be formated, since it's not in tiny font in the specs.
    00826            * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font.*/
    00827           SetDParam(0, tbl->legend);
    00828           assert(tbl->type < NUM_INDUSTRYTYPES);
    00829           SetDParam(1, _industry_counts[tbl->type]);
    00830           if (!tbl->show_on_map) {
    00831             /* Simply draw the string, not the black border of the legend color.
    00832              * This will enforce the idea of the disabled item */
    00833             DrawString(x + 11, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
    00834           } else {
    00835             DrawString(x + 11, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
    00836             GfxFillRect(x, y + 1, x + 8, y + 5, 0); // outer border of the legend color
    00837           }
    00838         } else {
    00839           /* Anything that is not an industry is using normal process */
    00840           GfxFillRect(x, y + 1, x + 8, y + 5, 0);
    00841           DrawString(x + 11, y, tbl->legend, TC_FROMSTRING);
    00842         }
    00843         GfxFillRect(x + 1, y + 2, x + 7, y + 4, tbl->colour); // legend color
    00844
    00845         tbl += 1;
    00846         y += 6;
    00847
    00848         if (tbl->end) { // end of the list
    00849           break;
    00850         } else if (tbl->col_break) {
    00851           /*  break asked, continue at top, 123 pixels (one "row") to the right */
    00852           x += 123;
    00853           y = y_org;
    00854         }
    00855       }
    00856
    00857       if (!FillDrawPixelInfo(&new_dpi, 3, 17, w->width - 28 + 22, w->height - 64 - 11 - diff))
    00858         return;
    00859
    00860       DrawSmallMap(&new_dpi, w, _smallmap_type, _smallmap_show_towns);
    00861     } break;
    00862
    00863     case WE_CLICK:
    00864       switch (e->we.click.widget) {
    00865         case SM_WIDGET_MAP: { // Map window
    00866           Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
    00867           Point pt;
    00868
    00869           /*
    00870            * XXX: scrolling with the left mouse button is done by subsequently
    00871            * clicking with the left mouse button; clicking once centers the
    00872            * large map at the selected point. So by unclicking the left mouse
    00873            * button here, it gets reclicked during the next inputloop, which
    00874            * would make it look like the mouse is being dragged, while it is
    00875            * actually being (virtually) clicked every inputloop.
    00876            */
    00877           _left_button_clicked = false;
    00878
    00879           pt = RemapCoords(WP(w, smallmap_d).scroll_x, WP(w,smallmap_d).scroll_y, 0);
    00880           WP(w2, vp_d).follow_vehicle = INVALID_VEHICLE;
    00881           WP(w2, vp_d).dest_scrollpos_x = pt.x + ((_cursor.pos.x - w->left + 2) << 4) - (w2->viewport->virtual_width >> 1);
    00882           WP(w2, vp_d).dest_scrollpos_y = pt.y + ((_cursor.pos.y - w->top - 16) << 4) - (w2->viewport->virtual_height >> 1);
    00883
    00884           SetWindowDirty(w);
    00885         } break;
    00886
    00887         case SM_WIDGET_CONTOUR:    // Show land contours
    00888         case SM_WIDGET_VEHICLES:   // Show vehicles
    00889         case SM_WIDGET_INDUSTRIES: // Show industries
    00890         case SM_WIDGET_ROUTES:     // Show transport routes
    00891         case SM_WIDGET_VEGETATION: // Show vegetation
    00892         case SM_WIDGET_OWNERS:     // Show land owners
    00893           w->RaiseWidget(_smallmap_type + SM_WIDGET_CONTOUR);
    00894           _smallmap_type = e->we.click.widget - SM_WIDGET_CONTOUR;
    00895           w->LowerWidget(_smallmap_type + SM_WIDGET_CONTOUR);
    00896
    00897           SetWindowDirty(w);
    00898           SndPlayFx(SND_15_BEEP);
    00899           break;
    00900
    00901         case SM_WIDGET_CENTERMAP: // Center the smallmap again
    00902           SmallMapCenterOnCurrentPos(w);
    00903
    00904           SetWindowDirty(w);
    00905           SndPlayFx(SND_15_BEEP);
    00906           break;
    00907
    00908         case SM_WIDGET_TOGGLETOWNNAME: // Toggle town names
    00909           w->ToggleWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME);
    00910           _smallmap_show_towns = w->IsWidgetLowered(SM_WIDGET_TOGGLETOWNNAME);
    00911
    00912           SetWindowDirty(w);
    00913           SndPlayFx(SND_15_BEEP);
    00914           break;
    00915
    00916         case SM_WIDGET_LEGEND: // Legend
    00917           /* if industry type small map*/
    00918           if (_smallmap_type == SMT_INDUSTRY) {
    00919             /* if click on industries label, find right industry type and enable/disable it */
    00920             Widget *wi = &w->widget[SM_WIDGET_LEGEND]; // label panel
    00921             uint column = (e->we.click.pt.x - 4) / 123;
    00922             uint line = (e->we.click.pt.y - wi->top - 2) / 6;
    00923             uint free = _smallmap_industry_count % 3;
    00924
    00925             if (column <= 3) {
    00926               /* check if click is on industry label*/
    00927               uint industry_pos = 0;
    00928               for (uint i = 0; i <= column; i++) {
    00929                 uint diff = (free > 0) ? 1 : 0;
    00930                 uint max_column_lines = _industries_per_column + diff;
    00931
    00932                 if (i < column) industry_pos = industry_pos + _industries_per_column + diff;
    00933
    00934                 if (i == column && line <= max_column_lines - 1) {
    00935                   industry_pos = industry_pos + line;
    00936                   _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
    00937                 }
    00938                 if( free > 0) free--;
    00939               }
    00940             }
    00941             /* Raise the two buttons "all", as we have done a specific choice */
    00942             w->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    00943             w->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    00944             SetWindowDirty(w);
    00945           }
    00946           break;
    00947
    00948         case SM_WIDGET_ENABLEINDUSTRIES: // Enable all industries
    00949           for (int i = 0; i != _smallmap_industry_count; i++) {
    00950             _legend_from_industries[i].show_on_map = true;
    00951           }
    00952           /* toggle appeareance indicating the choice */
    00953           w->LowerWidget(SM_WIDGET_ENABLEINDUSTRIES);
    00954           w->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
    00955           SetWindowDirty(w);
    00956           break;
    00957
    00958         case SM_WIDGET_DISABLEINDUSTRIES: // disable all industries
    00959           for (int i = 0; i != _smallmap_industry_count; i++) {
    00960             _legend_from_industries[i].show_on_map = false;
    00961           }
    00962           /* toggle appeareance indicating the choice */
    00963           w->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
    00964           w->LowerWidget(SM_WIDGET_DISABLEINDUSTRIES);
    00965           SetWindowDirty(w);
    00966           break;
    00967         }
    00968       break;
    00969
    00970     case WE_RCLICK:
    00971       if (e->we.click.widget == SM_WIDGET_MAP) {
    00972         if (_scrolling_viewport) return;
    00973         _scrolling_viewport = true;
    00974         _cursor.delta.x = 0;
    00975         _cursor.delta.y = 0;
    00976       }
    00977       break;
    00978
    00979     case WE_MOUSELOOP:
    00980       /* update the window every now and then */
    00981       if ((++w->vscroll.pos & 0x1F) == 0) SetWindowDirty(w);
    00982       break;
    00983
    00984     case WE_SCROLL: {
    00985       int x;
    00986       int y;
    00987       int sub;
    00988       int hx;
    00989       int hy;
    00990       int hvx;
    00991       int hvy;
    00992
    00993       _cursor.fix_at = true;
    00994
    00995       x = WP(w, smallmap_d).scroll_x;
    00996       y = WP(w, smallmap_d).scroll_y;
    00997
    00998       sub = WP(w, smallmap_d).subscroll + e->we.scroll.delta.x;
    00999
    01000       x -= (sub >> 2) << 4;
    01001       y += (sub >> 2) << 4;
    01002       sub &= 3;
    01003
    01004       x += (e->we.scroll.delta.y >> 1) << 4;
    01005       y += (e->we.scroll.delta.y >> 1) << 4;
    01006
    01007       if (e->we.scroll.delta.y & 1) {
    01008         x += TILE_SIZE;
    01009         sub += 2;
    01010         if (sub > 3) {
    01011           sub -= 4;
    01012           x -= TILE_SIZE;
    01013           y += TILE_SIZE;
    01014         }
    01015       }
    01016
    01017       hx = (w->widget[SM_WIDGET_MAP].right  - w->widget[SM_WIDGET_MAP].left) / 2;
    01018       hy = (w->widget[SM_WIDGET_MAP].bottom - w->widget[SM_WIDGET_MAP].top ) / 2;
    01019       hvx = hx * -4 + hy * 8;
    01020       hvy = hx *  4 + hy * 8;
    01021       if (x < -hvx) {
    01022         x = -hvx;
    01023         sub = 0;
    01024       }
    01025       if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
    01026         x = MapMaxX() * TILE_SIZE - hvx;
    01027         sub = 0;
    01028       }
    01029       if (y < -hvy) {
    01030         y = -hvy;
    01031         sub = 0;
    01032       }
    01033       if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
    01034         y = MapMaxY() * TILE_SIZE - hvy;
    01035         sub = 0;
    01036       }
    01037
    01038       WP(w, smallmap_d).scroll_x = x;
    01039       WP(w, smallmap_d).scroll_y = y;
    01040       WP(w, smallmap_d).subscroll = sub;
    01041
    01042       SetWindowDirty(w);
    01043     } break;
    01044   }
    01045 }
    01046
    01047 static const WindowDesc _smallmap_desc = {
    01048   WDP_AUTO, WDP_AUTO, 350, 214, 446, 314,
    01049   WC_SMALLMAP, WC_NONE,
    01050   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    01051   _smallmap_widgets,
    01052   SmallMapWindowProc
    01053 };
    01054
    01055 void ShowSmallMap()
    01056 {
    01057   Window *w;
    01058
    01059   w = AllocateWindowDescFront(&_smallmap_desc, 0);
    01060   if (w == NULL) return;
    01061
    01062   /* Resize the window to fit industries list */
    01063   if (_industries_per_column > BASE_NB_PER_COLUMN) {
    01064     uint diff = ((_industries_per_column - BASE_NB_PER_COLUMN) * BASE_NB_PER_COLUMN) + 1;
    01065
    01066     w->height = w->height + diff;
    01067
    01068     Widget *wi = &w->widget[SM_WIDGET_LEGEND]; // label panel
    01069     wi->bottom = wi->bottom + diff;
    01070
    01071     wi = &w->widget[SM_WIDGET_BUTTONSPANEL]; // filler panel under smallmap buttons
    01072     wi->bottom = wi->bottom + diff - 1;
    01073
    01074     /* Change widget position
    01075      * - footer panel
    01076      * - enable all industry
    01077      * - disable all industry
    01078      * - resize window button
    01079      */
    01080     for (uint i = SM_WIDGET_BOTTOMPANEL; i <= SM_WIDGET_RESIZEBOX; i++) {
    01081       wi           = &w->widget[i];
    01082       wi->top      = wi->top + diff;
    01083       wi->bottom   = wi->bottom + diff;
    01084     }
    01085   }
    01086
    01087   w->LowerWidget(_smallmap_type + SMT_OWNER);
    01088   w->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, _smallmap_show_towns);
    01089
    01090   SmallMapCenterOnCurrentPos(w);
    01091 }
    01092
    01093 /* Extra ViewPort Window Stuff */
    01094 static const Widget _extra_view_port_widgets[] = {
    01095 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,    0,   13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
    01096 {    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   287,    0,   13, STR_EXTRA_VIEW_PORT_TITLE,        STR_018C_WINDOW_TITLE_DRAG_THIS},
    01097 {  WWT_STICKYBOX,     RESIZE_LR,    14,   288,   299,    0,   13, 0x0,                              STR_STICKY_BUTTON},
    01098 {      WWT_PANEL,     RESIZE_RB,    14,     0,   299,   14,   33, 0x0,                              STR_NULL},
    01099 {      WWT_INSET,     RESIZE_RB,    14,     2,   297,   16,   31, 0x0,                              STR_NULL},
    01100 { WWT_PUSHIMGBTN,     RESIZE_TB,    14,     0,    21,   34,   55, SPR_IMG_ZOOMIN,                   STR_017F_ZOOM_THE_VIEW_IN},
    01101 { WWT_PUSHIMGBTN,     RESIZE_TB,    14,    22,    43,   34,   55, SPR_IMG_ZOOMOUT,                  STR_0180_ZOOM_THE_VIEW_OUT},
    01102 { WWT_PUSHTXTBTN,     RESIZE_TB,    14,    44,   171,   34,   55, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT},
    01103 { WWT_PUSHTXTBTN,     RESIZE_TB,    14,   172,   298,   34,   55, STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN, STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT},
    01104 {      WWT_PANEL,    RESIZE_RTB,    14,   299,   299,   34,   55, 0x0,                              STR_NULL},
    01105 {      WWT_PANEL,    RESIZE_RTB,    14,     0,   287,   56,   67, 0x0,                              STR_NULL},
    01106 {  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   288,   299,   56,   67, 0x0,                              STR_RESIZE_BUTTON},
    01107 {   WIDGETS_END},
    01108 };
    01109
    01110 static void ExtraViewPortWndProc(Window *w, WindowEvent *e)
    01111 {
    01112   switch (e->event) {
    01113   case WE_CREATE: /* Disable zoom in button */
    01114     /* New viewport start at (zero,zero) */
    01115     AssignWindowViewport(w, 3, 17, w->widget[4].right - w->widget[4].left - 1, w->widget[4].bottom - w->widget[4].top - 1, 0, ZOOM_LVL_VIEWPORT);
    01116
    01117     w->DisableWidget(5);
    01118     break;
    01119
    01120   case WE_PAINT:
    01121     // set the number in the title bar
    01122     SetDParam(0, w->window_number + 1);
    01123
    01124     DrawWindowWidgets(w);
    01125     DrawWindowViewport(w);
    01126     break;
    01127
    01128   case WE_CLICK:
    01129     switch (e->we.click.widget) {
    01130       case 5: DoZoomInOutWindow(ZOOM_IN,  w); break;
    01131       case 6: DoZoomInOutWindow(ZOOM_OUT, w); break;
    01132
    01133     case 7: { /* location button (move main view to same spot as this view) 'Paste Location' */
    01134       Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
    01135       int x = WP(w, vp_d).scrollpos_x; // Where is the main looking at
    01136       int y = WP(w, vp_d).scrollpos_y;
    01137
    01138       /* set this view to same location. Based on the center, adjusting for zoom */
    01139       WP(w2, vp_d).dest_scrollpos_x =  x - (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
    01140       WP(w2, vp_d).dest_scrollpos_y =  y - (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
    01141     } break;
    01142
    01143     case 8: { /* inverse location button (move this view to same spot as main view) 'Copy Location' */
    01144       const Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
    01145       int x = WP(w2, const vp_d).scrollpos_x;
    01146       int y = WP(w2, const vp_d).scrollpos_y;
    01147
    01148       WP(w, vp_d).dest_scrollpos_x =  x + (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
    01149       WP(w, vp_d).dest_scrollpos_y =  y + (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
    01150     } break;
    01151     }
    01152     break;
    01153
    01154   case WE_RESIZE:
    01155     w->viewport->width          += e->we.sizing.diff.x;
    01156     w->viewport->height         += e->we.sizing.diff.y;
    01157     w->viewport->virtual_width  += e->we.sizing.diff.x;
    01158     w->viewport->virtual_height += e->we.sizing.diff.y;
    01159     break;
    01160
    01161     case WE_SCROLL: {
    01162       ViewPort *vp = IsPtInWindowViewport(w, _cursor.pos.x, _cursor.pos.y);
    01163
    01164       if (vp == NULL) {
    01165         _cursor.fix_at = false;
    01166         _scrolling_viewport = false;
    01167       }
    01168
    01169       WP(w, vp_d).scrollpos_x += ScaleByZoom(e->we.scroll.delta.x, vp->zoom);
    01170       WP(w, vp_d).scrollpos_y += ScaleByZoom(e->we.scroll.delta.y, vp->zoom);
    01171       WP(w, vp_d).dest_scrollpos_x = WP(w, vp_d).scrollpos_x;
    01172       WP(w, vp_d).dest_scrollpos_y = WP(w, vp_d).scrollpos_y;
    01173     } break;
    01174
    01175     case WE_MOUSEWHEEL:
    01176       ZoomInOrOutToCursorWindow(e->we.wheel.wheel < 0, w);
    01177       break;
    01178
    01179
    01180     case WE_MESSAGE:
    01181       /* Only handle zoom message if intended for us (msg ZOOM_IN/ZOOM_OUT) */
    01182       if (e->we.message.wparam != w->window_number) break;
    01183       HandleZoomMessage(w, w->viewport, 5, 6);
    01184       break;
    01185   }
    01186 }
    01187
    01188 static const WindowDesc _extra_view_port_desc = {
    01189   WDP_AUTO, WDP_AUTO, 300, 68, 300, 268,
    01190   WC_EXTRA_VIEW_PORT, WC_NONE,
    01191   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    01192   _extra_view_port_widgets,
    01193   ExtraViewPortWndProc
    01194 };
    01195
    01196 void ShowExtraViewPortWindow()
    01197 {
    01198   Window *w, *v;
    01199   int i = 0;
    01200
    01201   /* find next free window number for extra viewport */
    01202   while (FindWindowById(WC_EXTRA_VIEW_PORT, i) != NULL) i++;
    01203
    01204   w = AllocateWindowDescFront(&_extra_view_port_desc, i);
    01205   if (w != NULL) {
    01206     int x, y;
    01207     /* the main window with the main view */
    01208     v = FindWindowById(WC_MAIN_WINDOW, 0);
    01209
    01210     /* center on same place as main window (zoom is maximum, no adjustment needed) */
    01211     x = WP(v, vp_d).scrollpos_x;
    01212     y = WP(v, vp_d).scrollpos_y;
    01213     WP(w, vp_d).scrollpos_x = x + (v->viewport->virtual_width  - (w->widget[4].right - w->widget[4].left) - 1) / 2;
    01214     WP(w, vp_d).scrollpos_y = y + (v->viewport->virtual_height - (w->widget[4].bottom - w->widget[4].top) - 1) / 2;
    01215     WP(w, vp_d).dest_scrollpos_x = WP(w, vp_d).scrollpos_x;
    01216     WP(w, vp_d).dest_scrollpos_y = WP(w, vp_d).scrollpos_y;
    01217   }
    01218 }