src/smallmap_gui.cpp
changeset 5835 e0ff603ae0b7
parent 5726 8f399788f6c9
child 6144 5a0ffbf27ced
equal deleted inserted replaced
5834:7bf92d5a5a0f 5835:e0ff603ae0b7
       
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 #include "functions.h"
       
     6 #include "bridge_map.h"
       
     7 #include "clear_map.h"
       
     8 #include "industry_map.h"
       
     9 #include "station_map.h"
       
    10 #include "table/strings.h"
       
    11 #include "table/sprites.h"
       
    12 #include "map.h"
       
    13 #include "tile.h"
       
    14 #include "gui.h"
       
    15 #include "tree_map.h"
       
    16 #include "tunnel_map.h"
       
    17 #include "window.h"
       
    18 #include "gfx.h"
       
    19 #include "viewport.h"
       
    20 #include "player.h"
       
    21 #include "vehicle.h"
       
    22 #include "town.h"
       
    23 #include "sound.h"
       
    24 #include "variables.h"
       
    25 
       
    26 static const Widget _smallmap_widgets[] = {
       
    27 {  WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},
       
    28 {   WWT_CAPTION,  RESIZE_RIGHT,    13,    11,   433,     0,    13, STR_00B0_MAP,            STR_018C_WINDOW_TITLE_DRAG_THIS},
       
    29 { WWT_STICKYBOX,     RESIZE_LR,    13,   434,   445,     0,    13, 0x0,                     STR_STICKY_BUTTON},
       
    30 {     WWT_PANEL,     RESIZE_RB,    13,     0,   445,    14,   257, 0x0,                     STR_NULL},
       
    31 {     WWT_INSET,     RESIZE_RB,    13,     2,   443,    16,   255, 0x0,                     STR_NULL},
       
    32 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   380,   401,   258,   279, SPR_IMG_SHOW_COUNTOURS,  STR_0191_SHOW_LAND_CONTOURS_ON_MAP},
       
    33 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   402,   423,   258,   279, SPR_IMG_SHOW_VEHICLES,   STR_0192_SHOW_VEHICLES_ON_MAP},
       
    34 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   424,   445,   258,   279, SPR_IMG_INDUSTRY,        STR_0193_SHOW_INDUSTRIES_ON_MAP},
       
    35 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   380,   401,   280,   301, SPR_IMG_SHOW_ROUTES,     STR_0194_SHOW_TRANSPORT_ROUTES_ON},
       
    36 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   402,   423,   280,   301, SPR_IMG_PLANTTREES,      STR_0195_SHOW_VEGETATION_ON_MAP},
       
    37 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   424,   445,   280,   301, SPR_IMG_COMPANY_GENERAL, STR_0196_SHOW_LAND_OWNERS_ON_MAP},
       
    38 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   358,   379,   258,   279, SPR_IMG_SMALLMAP,        STR_SMALLMAP_CENTER},
       
    39 {    WWT_IMGBTN,   RESIZE_LRTB,    13,   358,   379,   280,   301, SPR_IMG_TOWN,            STR_0197_TOGGLE_TOWN_NAMES_ON_OFF},
       
    40 {     WWT_PANEL,    RESIZE_RTB,    13,     0,   357,   258,   301, 0x0,                     STR_NULL},
       
    41 {     WWT_PANEL,    RESIZE_RTB,    13,     0,   433,   302,   313, 0x0,                     STR_NULL},
       
    42 { WWT_RESIZEBOX,   RESIZE_LRTB,    13,   434,   445,   302,   313, 0x0,                     STR_RESIZE_BUTTON},
       
    43 {  WIDGETS_END},
       
    44 };
       
    45 
       
    46 static int _smallmap_type;
       
    47 static bool _smallmap_show_towns = true;
       
    48 
       
    49 #define MK(a,b) a, b
       
    50 #define MKEND() 0xFFFF
       
    51 #define MS(a,b) (a | 0x100), b
       
    52 
       
    53 /* Legend text giving the colours to look for on the minimap */
       
    54 static const uint16 _legend_land_contours[] = {
       
    55 	MK(0x5A, STR_00F0_100M),
       
    56 	MK(0x5C, STR_00F1_200M),
       
    57 	MK(0x5E, STR_00F2_300M),
       
    58 	MK(0x1F, STR_00F3_400M),
       
    59 	MK(0x27, STR_00F4_500M),
       
    60 
       
    61 	MS(0xD7, STR_00EB_ROADS),
       
    62 	MK(0x0A, STR_00EC_RAILROADS),
       
    63 	MK(0x98, STR_00ED_STATIONS_AIRPORTS_DOCKS),
       
    64 	MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
       
    65 	MK(0x0F, STR_00EF_VEHICLES),
       
    66 	MKEND()
       
    67 };
       
    68 
       
    69 static const uint16 _legend_vehicles[] = {
       
    70 	MK(0xB8, STR_00F5_TRAINS),
       
    71 	MK(0xBF, STR_00F6_ROAD_VEHICLES),
       
    72 	MK(0x98, STR_00F7_SHIPS),
       
    73 	MK(0x0F, STR_00F8_AIRCRAFT),
       
    74 	MS(0xD7, STR_00F9_TRANSPORT_ROUTES),
       
    75 	MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
       
    76 	MKEND()
       
    77 };
       
    78 
       
    79 static const uint16 _legend_industries_normal[] = {
       
    80 	MK(0xD7, STR_00FA_COAL_MINE),
       
    81 	MK(0xB8, STR_00FB_POWER_STATION),
       
    82 	MK(0x56, STR_00FC_FOREST),
       
    83 	MK(0xC2, STR_00FD_SAWMILL),
       
    84 	MK(0xBF, STR_00FE_OIL_REFINERY),
       
    85 	MK(0x0F, STR_0105_BANK),
       
    86 
       
    87 	MS(0x30, STR_00FF_FARM),
       
    88 	MK(0xAE, STR_0100_FACTORY),
       
    89 	MK(0x98, STR_0102_OIL_WELLS),
       
    90 	MK(0x37, STR_0103_IRON_ORE_MINE),
       
    91 	MK(0x0A, STR_0104_STEEL_MILL),
       
    92 	MKEND()
       
    93 };
       
    94 
       
    95 static const uint16 _legend_industries_hilly[] = {
       
    96 	MK(0xD7, STR_00FA_COAL_MINE),
       
    97 	MK(0xB8, STR_00FB_POWER_STATION),
       
    98 	MK(0x56, STR_00FC_FOREST),
       
    99 	MK(0x0A, STR_0106_PAPER_MILL),
       
   100 	MK(0xBF, STR_00FE_OIL_REFINERY),
       
   101 	MK(0x37, STR_0108_FOOD_PROCESSING_PLANT),
       
   102 	MS(0x30, STR_00FF_FARM),
       
   103 
       
   104 	MK(0xAE, STR_0101_PRINTING_WORKS),
       
   105 	MK(0x98, STR_0102_OIL_WELLS),
       
   106 	MK(0xC2, STR_0107_GOLD_MINE),
       
   107 	MK(0x0F, STR_0105_BANK),
       
   108 	MKEND()
       
   109 };
       
   110 
       
   111 static const uint16 _legend_industries_desert[] = {
       
   112 	MK(0xBF, STR_00FE_OIL_REFINERY),
       
   113 	MK(0x98, STR_0102_OIL_WELLS),
       
   114 	MK(0x0F, STR_0105_BANK),
       
   115 	MK(0xB8, STR_0109_DIAMOND_MINE),
       
   116 	MK(0x37, STR_0108_FOOD_PROCESSING_PLANT),
       
   117 	MK(0x0A, STR_010A_COPPER_ORE_MINE),
       
   118 	MK(0x30, STR_00FF_FARM),
       
   119 	MS(0x56, STR_010B_FRUIT_PLANTATION),
       
   120 
       
   121 	MK(0x27, STR_010C_RUBBER_PLANTATION),
       
   122 	MK(0x25, STR_010D_WATER_SUPPLY),
       
   123 	MK(0xD0, STR_010E_WATER_TOWER),
       
   124 	MK(0xAE, STR_0100_FACTORY),
       
   125 	MK(0xC2, STR_010F_LUMBER_MILL),
       
   126 	MKEND()
       
   127 };
       
   128 
       
   129 static const uint16 _legend_industries_candy[] = {
       
   130 	MK(0x30, STR_0110_COTTON_CANDY_FOREST),
       
   131 	MK(0xAE, STR_0111_CANDY_FACTORY),
       
   132 	MK(0x27, STR_0112_BATTERY_FARM),
       
   133 	MK(0x37, STR_0113_COLA_WELLS),
       
   134 	MK(0xD0, STR_0114_TOY_SHOP),
       
   135 	MK(0x0A, STR_0115_TOY_FACTORY),
       
   136 	MS(0x25, STR_0116_PLASTIC_FOUNTAINS),
       
   137 
       
   138 	MK(0xB8, STR_0117_FIZZY_DRINK_FACTORY),
       
   139 	MK(0x98, STR_0118_BUBBLE_GENERATOR),
       
   140 	MK(0xC2, STR_0119_TOFFEE_QUARRY),
       
   141 	MK(0x0F, STR_011A_SUGAR_MINE),
       
   142 	MKEND()
       
   143 };
       
   144 
       
   145 static const uint16 _legend_routes[] = {
       
   146 	MK(0xD7, STR_00EB_ROADS),
       
   147 	MK(0x0A, STR_00EC_RAILROADS),
       
   148 	MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
       
   149 	MS(0x56, STR_011B_RAILROAD_STATION),
       
   150 
       
   151 	MK(0xC2, STR_011C_TRUCK_LOADING_BAY),
       
   152 	MK(0xBF, STR_011D_BUS_STATION),
       
   153 	MK(0xB8, STR_011E_AIRPORT_HELIPORT),
       
   154 	MK(0x98, STR_011F_DOCK),
       
   155 	MKEND()
       
   156 };
       
   157 
       
   158 static const uint16 _legend_vegetation[] = {
       
   159 	MK(0x52, STR_0120_ROUGH_LAND),
       
   160 	MK(0x54, STR_0121_GRASS_LAND),
       
   161 	MK(0x37, STR_0122_BARE_LAND),
       
   162 	MK(0x25, STR_0123_FIELDS),
       
   163 	MK(0x57, STR_0124_TREES),
       
   164 	MK(0xD0, STR_00FC_FOREST),
       
   165 	MS(0x0A, STR_0125_ROCKS),
       
   166 
       
   167 	MK(0xC2, STR_012A_DESERT),
       
   168 	MK(0x98, STR_012B_SNOW),
       
   169 	MK(0xD7, STR_00F9_TRANSPORT_ROUTES),
       
   170 	MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
       
   171 	MKEND()
       
   172 };
       
   173 
       
   174 static const uint16 _legend_land_owners[] = {
       
   175 	MK(0xCA, STR_0126_WATER),
       
   176 	MK(0x54, STR_0127_NO_OWNER),
       
   177 	MK(0xB4, STR_0128_TOWNS),
       
   178 	MK(0x20, STR_0129_INDUSTRIES),
       
   179 	MKEND()
       
   180 };
       
   181 #undef MK
       
   182 #undef MS
       
   183 #undef MKEND
       
   184 
       
   185 
       
   186 enum { IND_OFFS = 6 };
       
   187 static const uint16 * const _legend_table[] = {
       
   188 	_legend_land_contours,
       
   189 	_legend_vehicles,
       
   190 	NULL,
       
   191 	_legend_routes,
       
   192 	_legend_vegetation,
       
   193 	_legend_land_owners,
       
   194 
       
   195 	_legend_industries_normal,
       
   196 	_legend_industries_hilly,
       
   197 	_legend_industries_desert,
       
   198 	_legend_industries_candy,
       
   199 };
       
   200 
       
   201 #if defined(OTTD_ALIGNMENT)
       
   202 	static inline void WRITE_PIXELS(Pixel* d, uint32 val)
       
   203 	{
       
   204 #	if defined(TTD_BIG_ENDIAN)
       
   205 		d[0] = GB(val, 24, 8);
       
   206 		d[1] = GB(val, 16, 8);
       
   207 		d[2] = GB(val,  8, 8);
       
   208 		d[3] = GB(val,  0, 8);
       
   209 #	elif defined(TTD_LITTLE_ENDIAN)
       
   210 		d[0] = GB(val,  0, 8);
       
   211 		d[1] = GB(val,  8, 8);
       
   212 		d[2] = GB(val, 16, 8);
       
   213 		d[3] = GB(val, 24, 8);
       
   214 #	endif
       
   215 	}
       
   216 
       
   217 /* need to use OR, otherwise we will overwrite the wrong pixels at the edges :( */
       
   218 	static inline void WRITE_PIXELS_OR(Pixel *d, uint32 val)
       
   219 	{
       
   220 #	if defined(TTD_BIG_ENDIAN)
       
   221 		d[0] |= GB(val, 24, 8);
       
   222 		d[1] |= GB(val, 16, 8);
       
   223 		d[2] |= GB(val,  8, 8);
       
   224 		d[3] |= GB(val,  0, 8);
       
   225 #	elif defined(TTD_LITTLE_ENDIAN)
       
   226 		d[0] |= GB(val,  0, 8);
       
   227 		d[1] |= GB(val,  8, 8);
       
   228 		d[2] |= GB(val, 16, 8);
       
   229 		d[3] |= GB(val, 24, 8);
       
   230 #	endif
       
   231 	}
       
   232 #else
       
   233 #	define WRITE_PIXELS(dst, val)   *(uint32*)(dst) = (val);
       
   234 #	define WRITE_PIXELS_OR(dst,val) *(uint32*)(dst) |= (val);
       
   235 #endif
       
   236 
       
   237 #define MKCOLOR(x) TO_LE32X(x)
       
   238 
       
   239 /* Height encodings; 16 levels XXX - needs updating for more/finer heights! */
       
   240 static const uint32 _map_height_bits[16] = {
       
   241 	MKCOLOR(0x5A5A5A5A),
       
   242 	MKCOLOR(0x5A5B5A5B),
       
   243 	MKCOLOR(0x5B5B5B5B),
       
   244 	MKCOLOR(0x5B5C5B5C),
       
   245 	MKCOLOR(0x5C5C5C5C),
       
   246 	MKCOLOR(0x5C5D5C5D),
       
   247 	MKCOLOR(0x5D5D5D5D),
       
   248 	MKCOLOR(0x5D5E5D5E),
       
   249 	MKCOLOR(0x5E5E5E5E),
       
   250 	MKCOLOR(0x5E5F5E5F),
       
   251 	MKCOLOR(0x5F5F5F5F),
       
   252 	MKCOLOR(0x5F1F5F1F),
       
   253 	MKCOLOR(0x1F1F1F1F),
       
   254 	MKCOLOR(0x1F271F27),
       
   255 	MKCOLOR(0x27272727),
       
   256 	MKCOLOR(0x27272727),
       
   257 };
       
   258 
       
   259 typedef struct AndOr {
       
   260 	uint32 mor;
       
   261 	uint32 mand;
       
   262 } AndOr;
       
   263 
       
   264 static inline uint32 ApplyMask(uint32 colour, const AndOr *mask)
       
   265 {
       
   266 	return (colour & mask->mand) | mask->mor;
       
   267 }
       
   268 
       
   269 
       
   270 static const AndOr _smallmap_contours_andor[] = {
       
   271 	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
       
   272 	{MKCOLOR(0x000A0A00), MKCOLOR(0xFF0000FF)},
       
   273 	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
       
   274 	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
       
   275 	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
       
   276 	{MKCOLOR(0x98989898), MKCOLOR(0x00000000)},
       
   277 	{MKCOLOR(0xCACACACA), MKCOLOR(0x00000000)},
       
   278 	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
       
   279 	{MKCOLOR(0xB5B5B5B5), MKCOLOR(0x00000000)},
       
   280 	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
       
   281 	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
       
   282 	{MKCOLOR(0x000A0A00), MKCOLOR(0xFF0000FF)},
       
   283 };
       
   284 
       
   285 static const AndOr _smallmap_vehicles_andor[] = {
       
   286 	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
       
   287 	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
       
   288 	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
       
   289 	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
       
   290 	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
       
   291 	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
       
   292 	{MKCOLOR(0xCACACACA), MKCOLOR(0x00000000)},
       
   293 	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
       
   294 	{MKCOLOR(0xB5B5B5B5), MKCOLOR(0x00000000)},
       
   295 	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
       
   296 	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
       
   297 	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
       
   298 };
       
   299 
       
   300 static const AndOr _smallmap_vegetation_andor[] = {
       
   301 	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
       
   302 	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
       
   303 	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
       
   304 	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
       
   305 	{MKCOLOR(0x00575700), MKCOLOR(0xFF0000FF)},
       
   306 	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
       
   307 	{MKCOLOR(0xCACACACA), MKCOLOR(0x00000000)},
       
   308 	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
       
   309 	{MKCOLOR(0xB5B5B5B5), MKCOLOR(0x00000000)},
       
   310 	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
       
   311 	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
       
   312 	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
       
   313 };
       
   314 
       
   315 typedef uint32 GetSmallMapPixels(TileIndex tile); // typedef callthrough function
       
   316 
       
   317 /**
       
   318  * Draws one column of the small map in a certain mode onto the screen buffer. This
       
   319  * function looks exactly the same for all types
       
   320  *
       
   321  * @param dst Pointer to a part of the screen buffer to write to.
       
   322  * @param xc The X coordinate of the first tile in the column.
       
   323  * @param yc The Y coordinate of the first tile in the column
       
   324  * @param pitch Number of pixels to advance in the screen buffer each time a pixel is written.
       
   325  * @param reps Number of lines to draw
       
   326  * @param mask ?
       
   327  * @param proc Pointer to the colour function
       
   328  * @see GetSmallMapPixels(TileIndex)
       
   329  */
       
   330 static void DrawSmallMapStuff(Pixel *dst, uint xc, uint yc, int pitch, int reps, uint32 mask, GetSmallMapPixels *proc)
       
   331 {
       
   332 	Pixel *dst_ptr_end = _screen.dst_ptr + _screen.width * _screen.height - _screen.width;
       
   333 
       
   334 	do {
       
   335 		// check if the tile (xc,yc) is within the map range
       
   336 		if (xc < MapMaxX() && yc < MapMaxY()) {
       
   337 			// check if the dst pointer points to a pixel inside the screen buffer
       
   338 			if (dst > _screen.dst_ptr && dst < dst_ptr_end)
       
   339 				WRITE_PIXELS_OR(dst, proc(TileXY(xc, yc)) & mask);
       
   340 		}
       
   341 	// switch to next tile in the column
       
   342 	} while (xc++, yc++, dst += pitch, --reps != 0);
       
   343 }
       
   344 
       
   345 
       
   346 static inline TileType GetEffectiveTileType(TileIndex tile)
       
   347 {
       
   348 	TileType t = GetTileType(tile);
       
   349 
       
   350 	if (t == MP_TUNNELBRIDGE) {
       
   351 		TransportType tt;
       
   352 
       
   353 		if (IsTunnel(tile)) {
       
   354 			tt = GetTunnelTransportType(tile);
       
   355 		} else {
       
   356 			tt = GetBridgeTransportType(tile);
       
   357 		}
       
   358 		switch (tt) {
       
   359 			case TRANSPORT_RAIL: t = MP_RAILWAY; break;
       
   360 			case TRANSPORT_ROAD: t = MP_STREET;  break;
       
   361 			default:             t = MP_WATER;   break;
       
   362 		}
       
   363 	}
       
   364 	return t;
       
   365 }
       
   366 
       
   367 /**
       
   368  * Return the color a tile would be displayed with in the small map in mode "Contour".
       
   369  * @param tile The tile of which we would like to get the color.
       
   370  * @return The color of tile in the small map in mode "Contour"
       
   371  */
       
   372 static inline uint32 GetSmallMapContoursPixels(TileIndex tile)
       
   373 {
       
   374 	TileType t = GetEffectiveTileType(tile);
       
   375 
       
   376 	return
       
   377 		ApplyMask(_map_height_bits[TileHeight(tile)], &_smallmap_contours_andor[t]);
       
   378 }
       
   379 
       
   380 /**
       
   381  * Return the color a tile would be displayed with in the small map in mode "Vehicles".
       
   382  *
       
   383  * @param t The tile of which we would like to get the color.
       
   384  * @return The color of tile in the small map in mode "Vehicles"
       
   385  */
       
   386 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile)
       
   387 {
       
   388 	TileType t = GetEffectiveTileType(tile);
       
   389 
       
   390 	return ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
       
   391 }
       
   392 
       
   393 /* Industry colours... a total of 175 gfx - XXX - increase if more industries */
       
   394 static const byte _industry_smallmap_colors[175] = {
       
   395 	215, 215, 215, 215, 215, 215, 215, 184,
       
   396 	184, 184, 184, 194, 194, 194, 194, 194,
       
   397 	 86,  86, 191, 191, 191, 191, 191, 191,
       
   398 	152, 152, 152, 152, 152, 152, 152, 152,
       
   399 	152,  48,  48,  48,  48,  48,  48, 174,
       
   400 	174, 174, 174, 174, 174, 174, 174,  10,
       
   401 	 10,  10,  10,  10,  10,  10,  10,  10,
       
   402 	 10,  10,  15,  15,  55,  55,  55,  55,
       
   403 	 10,  10,  10,  10,  10,  10,  10,  10,
       
   404 	194, 194, 194, 194, 194, 194, 194, 194,
       
   405 	194, 194, 194, 194, 194, 194, 194, 194,
       
   406 	194,  15,  15, 184, 184, 184, 184, 184,
       
   407 	184, 184, 184, 184,  55,  55,  55,  55,
       
   408 	 55,  55,  55,  55,  55,  55,  55,  55,
       
   409 	 55,  55,  55,  55,  86,  39,  37,  37,
       
   410 	208, 174, 174, 174, 174, 194, 194, 194,
       
   411 	194,  48,  48, 174, 174, 174, 174,  39,
       
   412 	 39,  55, 208, 208, 208, 208,  10,  10,
       
   413 	 10,  10,  10,  10,  37,  37,  37,  37,
       
   414 	 37,  37,  37,  37, 184, 184, 184, 184,
       
   415 	152, 152, 152, 152, 194, 194, 194,  15,
       
   416 	 15,  15,  15,  15,  15,  15,  15,
       
   417 };
       
   418 
       
   419 /**
       
   420  * Return the color a tile would be displayed with in the small map in mode "Industries".
       
   421  *
       
   422  * @param tile The tile of which we would like to get the color.
       
   423  * @return The color of tile in the small map in mode "Industries"
       
   424  */
       
   425 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile)
       
   426 {
       
   427 	TileType t = GetEffectiveTileType(tile);
       
   428 
       
   429 	if (t == MP_INDUSTRY) {
       
   430 		return _industry_smallmap_colors[GetIndustryGfx(tile)] * 0x01010101;
       
   431 	}
       
   432 
       
   433 	return ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
       
   434 }
       
   435 
       
   436 /**
       
   437  * Return the color a tile would be displayed with in the small map in mode "Routes".
       
   438  *
       
   439  * @param t The tile of which we would like to get the color.
       
   440  * @return The color of tile  in the small map in mode "Routes"
       
   441  */
       
   442 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile)
       
   443 {
       
   444 	TileType t = GetEffectiveTileType(tile);
       
   445 	uint32 bits;
       
   446 
       
   447 	if (t == MP_STATION) {
       
   448 		switch (GetStationType(tile)) {
       
   449 			case STATION_RAIL:    bits = MKCOLOR(0x56565656); break;
       
   450 			case STATION_AIRPORT: bits = MKCOLOR(0xB8B8B8B8); break;
       
   451 			case STATION_TRUCK:   bits = MKCOLOR(0xC2C2C2C2); break;
       
   452 			case STATION_BUS:     bits = MKCOLOR(0xBFBFBFBF); break;
       
   453 			case STATION_DOCK:    bits = MKCOLOR(0x98989898); break;
       
   454 			default:              bits = MKCOLOR(0xFFFFFFFF); break;
       
   455 		}
       
   456 	} else {
       
   457 		// ground color
       
   458 		bits = ApplyMask(MKCOLOR(0x54545454), &_smallmap_contours_andor[t]);
       
   459 	}
       
   460 	return bits;
       
   461 }
       
   462 
       
   463 
       
   464 static const uint32 _vegetation_clear_bits[] = {
       
   465 	MKCOLOR(0x54545454), ///< full grass
       
   466 	MKCOLOR(0x52525252), ///< rough land
       
   467 	MKCOLOR(0x0A0A0A0A), ///< rocks
       
   468 	MKCOLOR(0x25252525), ///< fields
       
   469 	MKCOLOR(0x98989898), ///< snow
       
   470 	MKCOLOR(0xC2C2C2C2), ///< desert
       
   471 	MKCOLOR(0x54545454), ///< unused
       
   472 	MKCOLOR(0x54545454), ///< unused
       
   473 };
       
   474 
       
   475 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile)
       
   476 {
       
   477 	TileType t = GetEffectiveTileType(tile);
       
   478 	uint32 bits;
       
   479 
       
   480 	switch (t) {
       
   481 		case MP_CLEAR:
       
   482 			if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) {
       
   483 				bits = MKCOLOR(0x37373737);
       
   484 			} else {
       
   485 				bits = _vegetation_clear_bits[GetClearGround(tile)];
       
   486 			}
       
   487 			break;
       
   488 
       
   489 		case MP_INDUSTRY:
       
   490 			bits = GetIndustryType(tile) == IT_FOREST ? MKCOLOR(0xD0D0D0D0) : MKCOLOR(0xB5B5B5B5);
       
   491 			break;
       
   492 
       
   493 		case MP_TREES:
       
   494 			if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT) {
       
   495 				bits = (_opt.landscape == LT_HILLY) ? MKCOLOR(0x98575798) : MKCOLOR(0xC25757C2);
       
   496 			} else {
       
   497 				bits = MKCOLOR(0x54575754);
       
   498 			}
       
   499 			break;
       
   500 
       
   501 		default:
       
   502 			bits = ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
       
   503 			break;
       
   504 	}
       
   505 
       
   506 	return bits;
       
   507 }
       
   508 
       
   509 
       
   510 static uint32 _owner_colors[OWNER_END + 1];
       
   511 
       
   512 /**
       
   513  * Return the color a tile would be displayed with in the small map in mode "Owner".
       
   514  *
       
   515  * @param t The tile of which we would like to get the color.
       
   516  * @return The color of tile in the small map in mode "Owner"
       
   517  */
       
   518 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile)
       
   519 {
       
   520 	Owner o;
       
   521 
       
   522 	switch (GetTileType(tile)) {
       
   523 		case MP_INDUSTRY: o = OWNER_END;          break;
       
   524 		case MP_HOUSE:    o = OWNER_TOWN;         break;
       
   525 		default:          o = GetTileOwner(tile); break;
       
   526 	}
       
   527 
       
   528 	return _owner_colors[o];
       
   529 }
       
   530 
       
   531 
       
   532 static const uint32 _smallmap_mask_left[3] = {
       
   533 	MKCOLOR(0xFF000000),
       
   534 	MKCOLOR(0xFFFF0000),
       
   535 	MKCOLOR(0xFFFFFF00),
       
   536 };
       
   537 
       
   538 static const uint32 _smallmap_mask_right[] = {
       
   539 	MKCOLOR(0x000000FF),
       
   540 	MKCOLOR(0x0000FFFF),
       
   541 	MKCOLOR(0x00FFFFFF),
       
   542 };
       
   543 
       
   544 /* each tile has 4 x pixels and 1 y pixel */
       
   545 
       
   546 static GetSmallMapPixels *_smallmap_draw_procs[] = {
       
   547 	GetSmallMapContoursPixels,
       
   548 	GetSmallMapVehiclesPixels,
       
   549 	GetSmallMapIndustriesPixels,
       
   550 	GetSmallMapRoutesPixels,
       
   551 	GetSmallMapVegetationPixels,
       
   552 	GetSmallMapOwnerPixels,
       
   553 };
       
   554 
       
   555 static const byte _vehicle_type_colors[6] = {
       
   556 	184, 191, 152, 15, 215, 184
       
   557 };
       
   558 
       
   559 
       
   560 static void DrawVertMapIndicator(int x, int y, int x2, int y2)
       
   561 {
       
   562 	GfxFillRect(x, y,      x2, y + 3, 69);
       
   563 	GfxFillRect(x, y2 - 3, x2, y2,    69);
       
   564 }
       
   565 
       
   566 static void DrawHorizMapIndicator(int x, int y, int x2, int y2)
       
   567 {
       
   568 	GfxFillRect(x,      y, x + 3, y2, 69);
       
   569 	GfxFillRect(x2 - 3, y, x2,    y2, 69);
       
   570 }
       
   571 
       
   572 /**
       
   573  * Draws the small map.
       
   574  *
       
   575  * Basically, the small map is draw column of pixels by column of pixels. The pixels
       
   576  * are drawn directly into the screen buffer. The final map is drawn in multiple passes.
       
   577  * The passes are:
       
   578  * <ol><li>The colors of tiles in the different modes.</li>
       
   579  * <li>Town names (optional)</li>
       
   580  *
       
   581  * @param dpi pointer to pixel to write onto
       
   582  * @param w pointer to Window struct
       
   583  * @param type type of map requested (vegetation, owners, routes, etc)
       
   584  * @param show_towns true if the town names should be displayed, false if not.
       
   585  */
       
   586 static void DrawSmallMap(DrawPixelInfo *dpi, Window *w, int type, bool show_towns)
       
   587 {
       
   588 	DrawPixelInfo *old_dpi;
       
   589 	int dx,dy, x, y, x2, y2;
       
   590 	Pixel *ptr;
       
   591 	int tile_x;
       
   592 	int tile_y;
       
   593 	ViewPort *vp;
       
   594 
       
   595 	old_dpi = _cur_dpi;
       
   596 	_cur_dpi = dpi;
       
   597 
       
   598 	/* clear it */
       
   599 	GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
       
   600 
       
   601 	/* setup owner table */
       
   602 	if (type == 5) {
       
   603 		const Player *p;
       
   604 
       
   605 		/* fill with some special colors */
       
   606 		_owner_colors[OWNER_TOWN] = MKCOLOR(0xB4B4B4B4);
       
   607 		_owner_colors[OWNER_NONE] = MKCOLOR(0x54545454);
       
   608 		_owner_colors[OWNER_WATER] = MKCOLOR(0xCACACACA);
       
   609 		_owner_colors[OWNER_END]   = MKCOLOR(0x20202020); /* industry */
       
   610 
       
   611 		/* now fill with the player colors */
       
   612 		FOR_ALL_PLAYERS(p) {
       
   613 			if (p->is_active) {
       
   614 				_owner_colors[p->index] =
       
   615 					_colour_gradient[p->player_color][5] * 0x01010101;
       
   616 			}
       
   617 		}
       
   618 	}
       
   619 
       
   620 	tile_x = WP(w,smallmap_d).scroll_x / TILE_SIZE;
       
   621 	tile_y = WP(w,smallmap_d).scroll_y / TILE_SIZE;
       
   622 
       
   623 	dx = dpi->left + WP(w,smallmap_d).subscroll;
       
   624 	tile_x -= dx / 4;
       
   625 	tile_y += dx / 4;
       
   626 	dx &= 3;
       
   627 
       
   628 	dy = dpi->top;
       
   629 	tile_x += dy / 2;
       
   630 	tile_y += dy / 2;
       
   631 
       
   632 	if (dy & 1) {
       
   633 		tile_x++;
       
   634 		dx += 2;
       
   635 		if (dx > 3) {
       
   636 			dx -= 4;
       
   637 			tile_x--;
       
   638 			tile_y++;
       
   639 		}
       
   640 	}
       
   641 
       
   642 	ptr = dpi->dst_ptr - dx - 4;
       
   643 	x = - dx - 4;
       
   644 	y = 0;
       
   645 
       
   646 	for (;;) {
       
   647 		uint32 mask = 0xFFFFFFFF;
       
   648 		int reps;
       
   649 		int t;
       
   650 
       
   651 		/* distance from left edge */
       
   652 		if (x < 0) {
       
   653 			if (x < -3) goto skip_column;
       
   654 			/* mask to use at the left edge */
       
   655 			mask = _smallmap_mask_left[x + 3];
       
   656 		}
       
   657 
       
   658 		/* distance from right edge */
       
   659 		t = dpi->width - x;
       
   660 		if (t < 4) {
       
   661 			if (t <= 0) break; /* exit loop */
       
   662 			/* mask to use at the right edge */
       
   663 			mask &= _smallmap_mask_right[t - 1];
       
   664 		}
       
   665 
       
   666 		/* number of lines */
       
   667 		reps = (dpi->height - y + 1) / 2;
       
   668 		if (reps > 0) {
       
   669 //			assert(ptr >= dpi->dst_ptr);
       
   670 			DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch * 2, reps, mask, _smallmap_draw_procs[type]);
       
   671 		}
       
   672 
       
   673 skip_column:
       
   674 		if (y == 0) {
       
   675 			tile_y++;
       
   676 			y++;
       
   677 			ptr += dpi->pitch;
       
   678 		} else {
       
   679 			tile_x--;
       
   680 			y--;
       
   681 			ptr -= dpi->pitch;
       
   682 		}
       
   683 		ptr += 2;
       
   684 		x += 2;
       
   685 	}
       
   686 
       
   687 	/* draw vehicles? */
       
   688 	if (type == 0 || type == 1) {
       
   689 		Vehicle *v;
       
   690 		bool skip;
       
   691 		byte color;
       
   692 
       
   693 		FOR_ALL_VEHICLES(v) {
       
   694 			if (v->type != VEH_Special &&
       
   695 					(v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0) {
       
   696 				// Remap into flat coordinates.
       
   697 				Point pt = RemapCoords(
       
   698 					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
       
   699 					v->y_pos / TILE_SIZE - WP(w,smallmap_d).scroll_y / TILE_SIZE, //    dtto
       
   700 					0);
       
   701 				x = pt.x;
       
   702 				y = pt.y;
       
   703 
       
   704 				// Check if y is out of bounds?
       
   705 				y -= dpi->top;
       
   706 				if (!IS_INT_INSIDE(y, 0, dpi->height)) continue;
       
   707 
       
   708 				// Default is to draw both pixels.
       
   709 				skip = false;
       
   710 
       
   711 				// Offset X coordinate
       
   712 				x -= WP(w,smallmap_d).subscroll + 3 + dpi->left;
       
   713 
       
   714 				if (x < 0) {
       
   715 					// if x+1 is 0, that means we're on the very left edge,
       
   716 					//  and should thus only draw a single pixel
       
   717 					if (++x != 0) continue;
       
   718 					skip = true;
       
   719 				} else if (x >= dpi->width - 1) {
       
   720 					// Check if we're at the very right edge, and if so draw only a single pixel
       
   721 					if (x != dpi->width - 1) continue;
       
   722 					skip = true;
       
   723 				}
       
   724 
       
   725 				// Calculate pointer to pixel and the color
       
   726 				ptr = dpi->dst_ptr + y * dpi->pitch + x;
       
   727 				color = (type == 1) ? _vehicle_type_colors[v->type-0x10] : 0xF;
       
   728 
       
   729 				// And draw either one or two pixels depending on clipping
       
   730 				ptr[0] = color;
       
   731 				if (!skip) ptr[1] = color;
       
   732 			}
       
   733 		}
       
   734 	}
       
   735 
       
   736 	if (show_towns) {
       
   737 		const Town *t;
       
   738 
       
   739 		FOR_ALL_TOWNS(t) {
       
   740 			// Remap the town coordinate
       
   741 			Point pt = RemapCoords(
       
   742 				(int)(TileX(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_x) / TILE_SIZE,
       
   743 				(int)(TileY(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_y) / TILE_SIZE,
       
   744 				0);
       
   745 			x = pt.x - WP(w,smallmap_d).subscroll + 3 - (t->sign.width_2 >> 1);
       
   746 			y = pt.y;
       
   747 
       
   748 			// Check if the town sign is within bounds
       
   749 			if (x + t->sign.width_2 > dpi->left &&
       
   750 					x < dpi->left + dpi->width &&
       
   751 					y + 6 > dpi->top &&
       
   752 					y < dpi->top + dpi->height) {
       
   753 				// And draw it.
       
   754 				SetDParam(0, t->index);
       
   755 				DrawString(x, y, STR_2056, 12);
       
   756 			}
       
   757 		}
       
   758 	}
       
   759 
       
   760 	// Draw map indicators
       
   761 	{
       
   762 		Point pt;
       
   763 
       
   764 		// Find main viewport.
       
   765 		vp = FindWindowById(WC_MAIN_WINDOW,0)->viewport;
       
   766 
       
   767 		pt = RemapCoords(WP(w, smallmap_d).scroll_x, WP(w, smallmap_d).scroll_y, 0);
       
   768 
       
   769 		x = vp->virtual_left - pt.x;
       
   770 		y = vp->virtual_top - pt.y;
       
   771 		x2 = (x + vp->virtual_width) / TILE_SIZE;
       
   772 		y2 = (y + vp->virtual_height) / TILE_SIZE;
       
   773 		x /= TILE_SIZE;
       
   774 		y /= TILE_SIZE;
       
   775 
       
   776 		x -= WP(w,smallmap_d).subscroll;
       
   777 		x2 -= WP(w,smallmap_d).subscroll;
       
   778 
       
   779 		DrawVertMapIndicator(x, y, x, y2);
       
   780 		DrawVertMapIndicator(x2, y, x2, y2);
       
   781 
       
   782 		DrawHorizMapIndicator(x, y, x2, y);
       
   783 		DrawHorizMapIndicator(x, y2, x2, y2);
       
   784 	}
       
   785 	_cur_dpi = old_dpi;
       
   786 }
       
   787 
       
   788 void SmallMapCenterOnCurrentPos(Window *w)
       
   789 {
       
   790 	int x, y;
       
   791 	ViewPort *vp;
       
   792 	vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
       
   793 
       
   794 	x  = ((vp->virtual_width  - (w->widget[4].right  - w->widget[4].left) * TILE_SIZE) / 2 + vp->virtual_left) / 4;
       
   795 	y  = ((vp->virtual_height - (w->widget[4].bottom - w->widget[4].top ) * TILE_SIZE) / 2 + vp->virtual_top ) / 2 - TILE_SIZE * 2;
       
   796 	WP(w, smallmap_d).scroll_x = (y - x) & ~0xF;
       
   797 	WP(w, smallmap_d).scroll_y = (x + y) & ~0xF;
       
   798 	SetWindowDirty(w);
       
   799 }
       
   800 
       
   801 static void SmallMapWindowProc(Window *w, WindowEvent *e)
       
   802 {
       
   803 	switch (e->event) {
       
   804 		case WE_PAINT: {
       
   805 			const uint16 *tbl;
       
   806 			int x, y, y_org;
       
   807 			DrawPixelInfo new_dpi;
       
   808 
       
   809 			/* draw the window */
       
   810 			SetDParam(0, STR_00E5_CONTOURS + _smallmap_type);
       
   811 			DrawWindowWidgets(w);
       
   812 
       
   813 			/* draw the legend */
       
   814 			tbl = _legend_table[(_smallmap_type != 2) ? _smallmap_type : (_opt.landscape + IND_OFFS)];
       
   815 			x = 4;
       
   816 			y_org = w->height - 44 - 11;
       
   817 			y = y_org;
       
   818 			for (;;) {
       
   819 				GfxFillRect(x,     y + 1, x + 8, y + 5, 0);
       
   820 				GfxFillRect(x + 1, y + 2, x + 7, y + 4, (byte)tbl[0]);
       
   821 				DrawString(x + 11, y, tbl[1], 0);
       
   822 
       
   823 				tbl += 2;
       
   824 				y += 6;
       
   825 
       
   826 				if (tbl[0] == 0xFFFF) {
       
   827 					break;
       
   828 				} else if (tbl[0] & 0x100) {
       
   829 					x += 123;
       
   830 					y = y_org;
       
   831 				}
       
   832 			}
       
   833 
       
   834 			if (!FillDrawPixelInfo(&new_dpi, 3, 17, w->width - 28 + 22, w->height - 64 - 11))
       
   835 				return;
       
   836 
       
   837 			DrawSmallMap(&new_dpi, w, _smallmap_type, _smallmap_show_towns);
       
   838 		} break;
       
   839 
       
   840 		case WE_CLICK:
       
   841 			switch (e->we.click.widget) {
       
   842 				case 4: { // Map window
       
   843 					Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
       
   844 					Point pt;
       
   845 
       
   846 					/*
       
   847 					 * XXX: scrolling with the left mouse button is done by subsequently
       
   848 					 * clicking with the left mouse button; clicking once centers the
       
   849 					 * large map at the selected point. So by unclicking the left mouse
       
   850 					 * button here, it gets reclicked during the next inputloop, which
       
   851 					 * would make it look like the mouse is being dragged, while it is
       
   852 					 * actually being (virtually) clicked every inputloop.
       
   853 					 */
       
   854 					_left_button_clicked = false;
       
   855 
       
   856 					pt = RemapCoords(WP(w,smallmap_d).scroll_x, WP(w,smallmap_d).scroll_y, 0);
       
   857 					WP(w2, vp_d).scrollpos_x = pt.x + ((_cursor.pos.x - w->left + 2) << 4) - (w2->viewport->virtual_width >> 1);
       
   858 					WP(w2, vp_d).scrollpos_y = pt.y + ((_cursor.pos.y - w->top - 16) << 4) - (w2->viewport->virtual_height >> 1);
       
   859 
       
   860 					SetWindowDirty(w);
       
   861 				} break;
       
   862 
       
   863 				case 5:  // Show land contours
       
   864 				case 6:  // Show vehicles
       
   865 				case 7:  // Show industries
       
   866 				case 8:  // Show transport routes
       
   867 				case 9:  // Show vegetation
       
   868 				case 10: // Show land owners
       
   869 					RaiseWindowWidget(w, _smallmap_type + 5);
       
   870 					_smallmap_type = e->we.click.widget - 5;
       
   871 					LowerWindowWidget(w, _smallmap_type + 5);
       
   872 
       
   873 					SetWindowDirty(w);
       
   874 					SndPlayFx(SND_15_BEEP);
       
   875 					break;
       
   876 
       
   877 				case 11: // Center the smallmap again
       
   878 					SmallMapCenterOnCurrentPos(w);
       
   879 
       
   880 					SetWindowDirty(w);
       
   881 					SndPlayFx(SND_15_BEEP);
       
   882 					break;
       
   883 
       
   884 				case 12: // Toggle town names
       
   885 					ToggleWidgetLoweredState(w, 12);
       
   886 					_smallmap_show_towns = IsWindowWidgetLowered(w, 12);
       
   887 
       
   888 					SetWindowDirty(w);
       
   889 					SndPlayFx(SND_15_BEEP);
       
   890 					break;
       
   891 				}
       
   892 			break;
       
   893 
       
   894 		case WE_RCLICK:
       
   895 			if (e->we.click.widget == 4) {
       
   896 				if (_scrolling_viewport) return;
       
   897 				_scrolling_viewport = true;
       
   898 				_cursor.delta.x = 0;
       
   899 				_cursor.delta.y = 0;
       
   900 			}
       
   901 			break;
       
   902 
       
   903 		case WE_MOUSELOOP:
       
   904 			/* update the window every now and then */
       
   905 			if ((++w->vscroll.pos & 0x1F) == 0) SetWindowDirty(w);
       
   906 			break;
       
   907 
       
   908 		case WE_SCROLL: {
       
   909 			int x;
       
   910 			int y;
       
   911 			int sub;
       
   912 			int hx;
       
   913 			int hy;
       
   914 			int hvx;
       
   915 			int hvy;
       
   916 
       
   917 			_cursor.fix_at = true;
       
   918 
       
   919 			x = WP(w, smallmap_d).scroll_x;
       
   920 			y = WP(w, smallmap_d).scroll_y;
       
   921 
       
   922 			sub = WP(w, smallmap_d).subscroll + e->we.scroll.delta.x;
       
   923 
       
   924 			x -= (sub >> 2) << 4;
       
   925 			y += (sub >> 2) << 4;
       
   926 			sub &= 3;
       
   927 
       
   928 			x += (e->we.scroll.delta.y >> 1) << 4;
       
   929 			y += (e->we.scroll.delta.y >> 1) << 4;
       
   930 
       
   931 			if (e->we.scroll.delta.y & 1) {
       
   932 				x += TILE_SIZE;
       
   933 				sub += 2;
       
   934 				if (sub > 3) {
       
   935 					sub -= 4;
       
   936 					x -= TILE_SIZE;
       
   937 					y += TILE_SIZE;
       
   938 				}
       
   939 			}
       
   940 
       
   941 			hx = (w->widget[4].right  - w->widget[4].left) / 2;
       
   942 			hy = (w->widget[4].bottom - w->widget[4].top ) / 2;
       
   943 			hvx = hx * -4 + hy * 8;
       
   944 			hvy = hx *  4 + hy * 8;
       
   945 			if (x < -hvx) {
       
   946 				x = -hvx;
       
   947 				sub = 0;
       
   948 			}
       
   949 			if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
       
   950 				x = MapMaxX() * TILE_SIZE - hvx;
       
   951 				sub = 0;
       
   952 			}
       
   953 			if (y < -hvy) {
       
   954 				y = -hvy;
       
   955 				sub = 0;
       
   956 			}
       
   957 			if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
       
   958 				y = MapMaxY() * TILE_SIZE - hvy;
       
   959 				sub = 0;
       
   960 			}
       
   961 
       
   962 			WP(w, smallmap_d).scroll_x = x;
       
   963 			WP(w, smallmap_d).scroll_y = y;
       
   964 			WP(w, smallmap_d).subscroll = sub;
       
   965 
       
   966 			SetWindowDirty(w);
       
   967 		} break;
       
   968 	}
       
   969 }
       
   970 
       
   971 static const WindowDesc _smallmap_desc = {
       
   972 	WDP_AUTO, WDP_AUTO, 446, 314,
       
   973 	WC_SMALLMAP,0,
       
   974 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
       
   975 	_smallmap_widgets,
       
   976 	SmallMapWindowProc
       
   977 };
       
   978 
       
   979 void ShowSmallMap(void)
       
   980 {
       
   981 	Window *w;
       
   982 
       
   983 	w = AllocateWindowDescFront(&_smallmap_desc, 0);
       
   984 	if (w == NULL) return;
       
   985 
       
   986 	LowerWindowWidget(w, _smallmap_type + 5);
       
   987 	SetWindowWidgetLoweredState(w, 12, _smallmap_show_towns);
       
   988 	w->resize.width = 350;
       
   989 	w->resize.height = 250;
       
   990 
       
   991 	SmallMapCenterOnCurrentPos(w);
       
   992 }
       
   993 
       
   994 /* Extra ViewPort Window Stuff */
       
   995 static const Widget _extra_view_port_widgets[] = {
       
   996 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
       
   997 {    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   287,     0,    13, STR_EXTRA_VIEW_PORT_TITLE,        STR_018C_WINDOW_TITLE_DRAG_THIS},
       
   998 {  WWT_STICKYBOX,     RESIZE_LR,    14,   288,   299,     0,    13, 0x0,                              STR_STICKY_BUTTON},
       
   999 {      WWT_PANEL,     RESIZE_RB,    14,     0,   299,    14,   233, 0x0,                              STR_NULL},
       
  1000 {      WWT_INSET,     RESIZE_RB,    14,     2,   297,    16,   231, 0x0,                              STR_NULL},
       
  1001 { WWT_PUSHIMGBTN,     RESIZE_TB,    14,     0,    21,   234,   255, SPR_IMG_ZOOMIN,                   STR_017F_ZOOM_THE_VIEW_IN},
       
  1002 { WWT_PUSHIMGBTN,     RESIZE_TB,    14,    22,    43,   234,   255, SPR_IMG_ZOOMOUT,                  STR_0180_ZOOM_THE_VIEW_OUT},
       
  1003 { WWT_PUSHTXTBTN,     RESIZE_TB,    14,    44,   171,   234,   255, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT},
       
  1004 { WWT_PUSHTXTBTN,     RESIZE_TB,    14,   172,   298,   234,   255, STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN, STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT},
       
  1005 {      WWT_PANEL,    RESIZE_RTB,    14,   299,   299,   234,   255, 0x0,                              STR_NULL},
       
  1006 {      WWT_PANEL,    RESIZE_RTB,    14,     0,   287,   256,   267, 0x0,                              STR_NULL},
       
  1007 {  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   288,   299,   256,   267, 0x0,                              STR_RESIZE_BUTTON},
       
  1008 {   WIDGETS_END},
       
  1009 };
       
  1010 
       
  1011 static void ExtraViewPortWndProc(Window *w, WindowEvent *e)
       
  1012 {
       
  1013 	switch (e->event) {
       
  1014 	case WE_CREATE: /* Disable zoom in button */
       
  1015 		DisableWindowWidget(w, 5);
       
  1016 		break;
       
  1017 
       
  1018 	case WE_PAINT:
       
  1019 		// set the number in the title bar
       
  1020 		SetDParam(0, w->window_number + 1);
       
  1021 
       
  1022 		DrawWindowWidgets(w);
       
  1023 		DrawWindowViewport(w);
       
  1024 		break;
       
  1025 
       
  1026 	case WE_CLICK:
       
  1027 		switch (e->we.click.widget) {
       
  1028 			case 5: DoZoomInOutWindow(ZOOM_IN,  w); break;
       
  1029 			case 6: DoZoomInOutWindow(ZOOM_OUT, w); break;
       
  1030 
       
  1031 		case 7: { /* location button (move main view to same spot as this view) 'Paste Location' */
       
  1032 			Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
       
  1033 			int x = WP(w, vp_d).scrollpos_x; // Where is the main looking at
       
  1034 			int y = WP(w, vp_d).scrollpos_y;
       
  1035 
       
  1036 			// set this view to same location. Based on the center, adjusting for zoom
       
  1037 			WP(w2, vp_d).scrollpos_x =  x - (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
       
  1038 			WP(w2, vp_d).scrollpos_y =  y - (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
       
  1039 		} break;
       
  1040 
       
  1041 		case 8: { /* inverse location button (move this view to same spot as main view) 'Copy Location' */
       
  1042 			const Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
       
  1043 			int x = WP(w2, const vp_d).scrollpos_x;
       
  1044 			int y = WP(w2, const vp_d).scrollpos_y;
       
  1045 
       
  1046 			WP(w, vp_d).scrollpos_x =  x + (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
       
  1047 			WP(w, vp_d).scrollpos_y =  y + (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
       
  1048 		} break;
       
  1049 		}
       
  1050 		break;
       
  1051 
       
  1052 	case WE_RESIZE:
       
  1053 		w->viewport->width          += e->we.sizing.diff.x;
       
  1054 		w->viewport->height         += e->we.sizing.diff.y;
       
  1055 		w->viewport->virtual_width  += e->we.sizing.diff.x;
       
  1056 		w->viewport->virtual_height += e->we.sizing.diff.y;
       
  1057 		break;
       
  1058 
       
  1059 		case WE_SCROLL: {
       
  1060 			ViewPort *vp = IsPtInWindowViewport(w, _cursor.pos.x, _cursor.pos.y);
       
  1061 
       
  1062 			if (vp == NULL) {
       
  1063 				_cursor.fix_at = false;
       
  1064 				_scrolling_viewport = false;
       
  1065 			}
       
  1066 
       
  1067 			WP(w, vp_d).scrollpos_x += e->we.scroll.delta.x << vp->zoom;
       
  1068 			WP(w, vp_d).scrollpos_y += e->we.scroll.delta.y << vp->zoom;
       
  1069 		} break;
       
  1070 
       
  1071 		case WE_MOUSEWHEEL:
       
  1072 			ZoomInOrOutToCursorWindow(e->we.wheel.wheel < 0, w);
       
  1073 			break;
       
  1074 
       
  1075 
       
  1076 		case WE_MESSAGE:
       
  1077 			/* Only handle zoom message if intended for us (msg ZOOM_IN/ZOOM_OUT) */
       
  1078 			if (e->we.message.wparam != w->window_number) break;
       
  1079 			HandleZoomMessage(w, w->viewport, 5, 6);
       
  1080 			break;
       
  1081 	}
       
  1082 }
       
  1083 
       
  1084 static const WindowDesc _extra_view_port_desc = {
       
  1085 	WDP_AUTO, WDP_AUTO, 300, 268,
       
  1086 	WC_EXTRA_VIEW_PORT,0,
       
  1087 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
       
  1088 	_extra_view_port_widgets,
       
  1089 	ExtraViewPortWndProc
       
  1090 };
       
  1091 
       
  1092 void ShowExtraViewPortWindow(void)
       
  1093 {
       
  1094 	Window *w, *v;
       
  1095 	int i = 0;
       
  1096 
       
  1097 	// find next free window number for extra viewport
       
  1098 	while (FindWindowById(WC_EXTRA_VIEW_PORT, i) != NULL) i++;
       
  1099 
       
  1100 	w = AllocateWindowDescFront(&_extra_view_port_desc, i);
       
  1101 	if (w != NULL) {
       
  1102 		int x, y;
       
  1103 		// the main window with the main view
       
  1104 		v = FindWindowById(WC_MAIN_WINDOW, 0);
       
  1105 		// New viewport start ats (zero,zero)
       
  1106 		AssignWindowViewport(w, 3, 17, 294, 214, 0 , 0);
       
  1107 
       
  1108 		// center on same place as main window (zoom is maximum, no adjustment needed)
       
  1109 		x = WP(v, vp_d).scrollpos_x;
       
  1110 		y = WP(v, vp_d).scrollpos_y;
       
  1111 		WP(w, vp_d).scrollpos_x = x + (v->viewport->virtual_width  - (294)) / 2;
       
  1112 		WP(w, vp_d).scrollpos_y = y + (v->viewport->virtual_height - (214)) / 2;
       
  1113 	}
       
  1114 }