src/smallmap_gui.c
branchcustombridgeheads
changeset 5649 55c8267c933f
parent 5648 1608018c5ff2
child 5650 aefc131bf5ce
equal deleted inserted replaced
5648:1608018c5ff2 5649:55c8267c933f
     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 	switch (GetTileType(tile)) {
       
   349 		case MP_STREET_BRIDGE:  return MP_STREET;
       
   350 		case MP_RAILWAY_BRIDGE: return MP_RAILWAY;
       
   351 		case MP_TUNNEL:
       
   352 			if (GetTunnelTransportType(tile) == TRANSPORT_RAIL) return MP_RAILWAY;
       
   353 			return MP_STREET;
       
   354 		default: return GetTileType(tile);
       
   355 	}
       
   356 }
       
   357 
       
   358 /**
       
   359  * Return the color a tile would be displayed with in the small map in mode "Contour".
       
   360  * @param tile The tile of which we would like to get the color.
       
   361  * @return The color of tile in the small map in mode "Contour"
       
   362  */
       
   363 static inline uint32 GetSmallMapContoursPixels(TileIndex tile)
       
   364 {
       
   365 	TileType t = GetEffectiveTileType(tile);
       
   366 
       
   367 	return
       
   368 		ApplyMask(_map_height_bits[TileHeight(tile)], &_smallmap_contours_andor[t]);
       
   369 }
       
   370 
       
   371 /**
       
   372  * Return the color a tile would be displayed with in the small map in mode "Vehicles".
       
   373  *
       
   374  * @param t The tile of which we would like to get the color.
       
   375  * @return The color of tile in the small map in mode "Vehicles"
       
   376  */
       
   377 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile)
       
   378 {
       
   379 	TileType t = GetEffectiveTileType(tile);
       
   380 
       
   381 	return ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
       
   382 }
       
   383 
       
   384 /* Industry colours... a total of 175 gfx - XXX - increase if more industries */
       
   385 static const byte _industry_smallmap_colors[175] = {
       
   386 	215, 215, 215, 215, 215, 215, 215, 184,
       
   387 	184, 184, 184, 194, 194, 194, 194, 194,
       
   388 	 86,  86, 191, 191, 191, 191, 191, 191,
       
   389 	152, 152, 152, 152, 152, 152, 152, 152,
       
   390 	152,  48,  48,  48,  48,  48,  48, 174,
       
   391 	174, 174, 174, 174, 174, 174, 174,  10,
       
   392 	 10,  10,  10,  10,  10,  10,  10,  10,
       
   393 	 10,  10,  15,  15,  55,  55,  55,  55,
       
   394 	 10,  10,  10,  10,  10,  10,  10,  10,
       
   395 	194, 194, 194, 194, 194, 194, 194, 194,
       
   396 	194, 194, 194, 194, 194, 194, 194, 194,
       
   397 	194,  15,  15, 184, 184, 184, 184, 184,
       
   398 	184, 184, 184, 184,  55,  55,  55,  55,
       
   399 	 55,  55,  55,  55,  55,  55,  55,  55,
       
   400 	 55,  55,  55,  55,  86,  39,  37,  37,
       
   401 	208, 174, 174, 174, 174, 194, 194, 194,
       
   402 	194,  48,  48, 174, 174, 174, 174,  39,
       
   403 	 39,  55, 208, 208, 208, 208,  10,  10,
       
   404 	 10,  10,  10,  10,  37,  37,  37,  37,
       
   405 	 37,  37,  37,  37, 184, 184, 184, 184,
       
   406 	152, 152, 152, 152, 194, 194, 194,  15,
       
   407 	 15,  15,  15,  15,  15,  15,  15,
       
   408 };
       
   409 
       
   410 /**
       
   411  * Return the color a tile would be displayed with in the small map in mode "Industries".
       
   412  *
       
   413  * @param tile The tile of which we would like to get the color.
       
   414  * @return The color of tile in the small map in mode "Industries"
       
   415  */
       
   416 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile)
       
   417 {
       
   418 	TileType t = GetEffectiveTileType(tile);
       
   419 
       
   420 	if (t == MP_INDUSTRY) {
       
   421 		return _industry_smallmap_colors[GetIndustryGfx(tile)] * 0x01010101;
       
   422 	}
       
   423 
       
   424 	return ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
       
   425 }
       
   426 
       
   427 /**
       
   428  * Return the color a tile would be displayed with in the small map in mode "Routes".
       
   429  *
       
   430  * @param t The tile of which we would like to get the color.
       
   431  * @return The color of tile  in the small map in mode "Routes"
       
   432  */
       
   433 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile)
       
   434 {
       
   435 	TileType t = GetEffectiveTileType(tile);
       
   436 	uint32 bits;
       
   437 
       
   438 	if (t == MP_STATION) {
       
   439 		switch (GetStationType(tile)) {
       
   440 			case STATION_RAIL:    bits = MKCOLOR(0x56565656); break;
       
   441 			case STATION_AIRPORT: bits = MKCOLOR(0xB8B8B8B8); break;
       
   442 			case STATION_TRUCK:   bits = MKCOLOR(0xC2C2C2C2); break;
       
   443 			case STATION_BUS:     bits = MKCOLOR(0xBFBFBFBF); break;
       
   444 			case STATION_DOCK:    bits = MKCOLOR(0x98989898); break;
       
   445 			default:              bits = MKCOLOR(0xFFFFFFFF); break;
       
   446 		}
       
   447 	} else {
       
   448 		// ground color
       
   449 		bits = ApplyMask(MKCOLOR(0x54545454), &_smallmap_contours_andor[t]);
       
   450 	}
       
   451 	return bits;
       
   452 }
       
   453 
       
   454 
       
   455 static const uint32 _vegetation_clear_bits[] = {
       
   456 	MKCOLOR(0x54545454), ///< full grass
       
   457 	MKCOLOR(0x52525252), ///< rough land
       
   458 	MKCOLOR(0x0A0A0A0A), ///< rocks
       
   459 	MKCOLOR(0x25252525), ///< fields
       
   460 	MKCOLOR(0x98989898), ///< snow
       
   461 	MKCOLOR(0xC2C2C2C2), ///< desert
       
   462 	MKCOLOR(0x54545454), ///< unused
       
   463 	MKCOLOR(0x54545454), ///< unused
       
   464 };
       
   465 
       
   466 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile)
       
   467 {
       
   468 	TileType t = GetEffectiveTileType(tile);
       
   469 	uint32 bits;
       
   470 
       
   471 	switch (t) {
       
   472 		case MP_CLEAR:
       
   473 			if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) {
       
   474 				bits = MKCOLOR(0x37373737);
       
   475 			} else {
       
   476 				bits = _vegetation_clear_bits[GetClearGround(tile)];
       
   477 			}
       
   478 			break;
       
   479 
       
   480 		case MP_INDUSTRY:
       
   481 			bits = GetIndustryType(tile) == IT_FOREST ? MKCOLOR(0xD0D0D0D0) : MKCOLOR(0xB5B5B5B5);
       
   482 			break;
       
   483 
       
   484 		case MP_TREES:
       
   485 			if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT) {
       
   486 				bits = (_opt.landscape == LT_HILLY) ? MKCOLOR(0x98575798) : MKCOLOR(0xC25757C2);
       
   487 			} else {
       
   488 				bits = MKCOLOR(0x54575754);
       
   489 			}
       
   490 			break;
       
   491 
       
   492 		default:
       
   493 			bits = ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
       
   494 			break;
       
   495 	}
       
   496 
       
   497 	return bits;
       
   498 }
       
   499 
       
   500 
       
   501 static uint32 _owner_colors[OWNER_END + 1];
       
   502 
       
   503 /**
       
   504  * Return the color a tile would be displayed with in the small map in mode "Owner".
       
   505  *
       
   506  * @param t The tile of which we would like to get the color.
       
   507  * @return The color of tile in the small map in mode "Owner"
       
   508  */
       
   509 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile)
       
   510 {
       
   511 	Owner o;
       
   512 
       
   513 	switch (GetTileType(tile)) {
       
   514 		case MP_INDUSTRY: o = OWNER_END;          break;
       
   515 		case MP_HOUSE:    o = OWNER_TOWN;         break;
       
   516 		default:          o = GetTileOwner(tile); break;
       
   517 	}
       
   518 
       
   519 	return _owner_colors[o];
       
   520 }
       
   521 
       
   522 
       
   523 static const uint32 _smallmap_mask_left[3] = {
       
   524 	MKCOLOR(0xFF000000),
       
   525 	MKCOLOR(0xFFFF0000),
       
   526 	MKCOLOR(0xFFFFFF00),
       
   527 };
       
   528 
       
   529 static const uint32 _smallmap_mask_right[] = {
       
   530 	MKCOLOR(0x000000FF),
       
   531 	MKCOLOR(0x0000FFFF),
       
   532 	MKCOLOR(0x00FFFFFF),
       
   533 };
       
   534 
       
   535 /* each tile has 4 x pixels and 1 y pixel */
       
   536 
       
   537 static GetSmallMapPixels *_smallmap_draw_procs[] = {
       
   538 	GetSmallMapContoursPixels,
       
   539 	GetSmallMapVehiclesPixels,
       
   540 	GetSmallMapIndustriesPixels,
       
   541 	GetSmallMapRoutesPixels,
       
   542 	GetSmallMapVegetationPixels,
       
   543 	GetSmallMapOwnerPixels,
       
   544 };
       
   545 
       
   546 static const byte _vehicle_type_colors[6] = {
       
   547 	184, 191, 152, 15, 215, 184
       
   548 };
       
   549 
       
   550 
       
   551 static void DrawVertMapIndicator(int x, int y, int x2, int y2)
       
   552 {
       
   553 	GfxFillRect(x, y,      x2, y + 3, 69);
       
   554 	GfxFillRect(x, y2 - 3, x2, y2,    69);
       
   555 }
       
   556 
       
   557 static void DrawHorizMapIndicator(int x, int y, int x2, int y2)
       
   558 {
       
   559 	GfxFillRect(x,      y, x + 3, y2, 69);
       
   560 	GfxFillRect(x2 - 3, y, x2,    y2, 69);
       
   561 }
       
   562 
       
   563 /**
       
   564  * Draws the small map.
       
   565  *
       
   566  * Basically, the small map is draw column of pixels by column of pixels. The pixels
       
   567  * are drawn directly into the screen buffer. The final map is drawn in multiple passes.
       
   568  * The passes are:
       
   569  * <ol><li>The colors of tiles in the different modes.</li>
       
   570  * <li>Town names (optional)</li>
       
   571  *
       
   572  * @param dpi pointer to pixel to write onto
       
   573  * @param w pointer to Window struct
       
   574  * @param type type of map requested (vegetation, owners, routes, etc)
       
   575  * @param show_towns true if the town names should be displayed, false if not.
       
   576  */
       
   577 static void DrawSmallMap(DrawPixelInfo *dpi, Window *w, int type, bool show_towns)
       
   578 {
       
   579 	DrawPixelInfo *old_dpi;
       
   580 	int dx,dy, x, y, x2, y2;
       
   581 	Pixel *ptr;
       
   582 	int tile_x;
       
   583 	int tile_y;
       
   584 	ViewPort *vp;
       
   585 
       
   586 	old_dpi = _cur_dpi;
       
   587 	_cur_dpi = dpi;
       
   588 
       
   589 	/* clear it */
       
   590 	GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
       
   591 
       
   592 	/* setup owner table */
       
   593 	if (type == 5) {
       
   594 		const Player *p;
       
   595 
       
   596 		/* fill with some special colors */
       
   597 		_owner_colors[OWNER_TOWN] = MKCOLOR(0xB4B4B4B4);
       
   598 		_owner_colors[OWNER_NONE] = MKCOLOR(0x54545454);
       
   599 		_owner_colors[OWNER_WATER] = MKCOLOR(0xCACACACA);
       
   600 		_owner_colors[OWNER_END]   = MKCOLOR(0x20202020); /* industry */
       
   601 
       
   602 		/* now fill with the player colors */
       
   603 		FOR_ALL_PLAYERS(p) {
       
   604 			if (p->is_active) {
       
   605 				_owner_colors[p->index] =
       
   606 					_colour_gradient[p->player_color][5] * 0x01010101;
       
   607 			}
       
   608 		}
       
   609 	}
       
   610 
       
   611 	tile_x = WP(w,smallmap_d).scroll_x / TILE_SIZE;
       
   612 	tile_y = WP(w,smallmap_d).scroll_y / TILE_SIZE;
       
   613 
       
   614 	dx = dpi->left + WP(w,smallmap_d).subscroll;
       
   615 	tile_x -= dx / 4;
       
   616 	tile_y += dx / 4;
       
   617 	dx &= 3;
       
   618 
       
   619 	dy = dpi->top;
       
   620 	tile_x += dy / 2;
       
   621 	tile_y += dy / 2;
       
   622 
       
   623 	if (dy & 1) {
       
   624 		tile_x++;
       
   625 		dx += 2;
       
   626 		if (dx > 3) {
       
   627 			dx -= 4;
       
   628 			tile_x--;
       
   629 			tile_y++;
       
   630 		}
       
   631 	}
       
   632 
       
   633 	ptr = dpi->dst_ptr - dx - 4;
       
   634 	x = - dx - 4;
       
   635 	y = 0;
       
   636 
       
   637 	for (;;) {
       
   638 		uint32 mask = 0xFFFFFFFF;
       
   639 		int reps;
       
   640 		int t;
       
   641 
       
   642 		/* distance from left edge */
       
   643 		if (x < 0) {
       
   644 			if (x < -3) goto skip_column;
       
   645 			/* mask to use at the left edge */
       
   646 			mask = _smallmap_mask_left[x + 3];
       
   647 		}
       
   648 
       
   649 		/* distance from right edge */
       
   650 		t = dpi->width - x;
       
   651 		if (t < 4) {
       
   652 			if (t <= 0) break; /* exit loop */
       
   653 			/* mask to use at the right edge */
       
   654 			mask &= _smallmap_mask_right[t - 1];
       
   655 		}
       
   656 
       
   657 		/* number of lines */
       
   658 		reps = (dpi->height - y + 1) / 2;
       
   659 		if (reps > 0) {
       
   660 //			assert(ptr >= dpi->dst_ptr);
       
   661 			DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch * 2, reps, mask, _smallmap_draw_procs[type]);
       
   662 		}
       
   663 
       
   664 skip_column:
       
   665 		if (y == 0) {
       
   666 			tile_y++;
       
   667 			y++;
       
   668 			ptr += dpi->pitch;
       
   669 		} else {
       
   670 			tile_x--;
       
   671 			y--;
       
   672 			ptr -= dpi->pitch;
       
   673 		}
       
   674 		ptr += 2;
       
   675 		x += 2;
       
   676 	}
       
   677 
       
   678 	/* draw vehicles? */
       
   679 	if (type == 0 || type == 1) {
       
   680 		Vehicle *v;
       
   681 		bool skip;
       
   682 		byte color;
       
   683 
       
   684 		FOR_ALL_VEHICLES(v) {
       
   685 			if (v->type != VEH_Special &&
       
   686 					(v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0) {
       
   687 				// Remap into flat coordinates.
       
   688 				Point pt = RemapCoords(
       
   689 					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
       
   690 					v->y_pos / TILE_SIZE - WP(w,smallmap_d).scroll_y / TILE_SIZE, //    dtto
       
   691 					0);
       
   692 				x = pt.x;
       
   693 				y = pt.y;
       
   694 
       
   695 				// Check if y is out of bounds?
       
   696 				y -= dpi->top;
       
   697 				if (!IS_INT_INSIDE(y, 0, dpi->height)) continue;
       
   698 
       
   699 				// Default is to draw both pixels.
       
   700 				skip = false;
       
   701 
       
   702 				// Offset X coordinate
       
   703 				x -= WP(w,smallmap_d).subscroll + 3 + dpi->left;
       
   704 
       
   705 				if (x < 0) {
       
   706 					// if x+1 is 0, that means we're on the very left edge,
       
   707 					//  and should thus only draw a single pixel
       
   708 					if (++x != 0) continue;
       
   709 					skip = true;
       
   710 				} else if (x >= dpi->width - 1) {
       
   711 					// Check if we're at the very right edge, and if so draw only a single pixel
       
   712 					if (x != dpi->width - 1) continue;
       
   713 					skip = true;
       
   714 				}
       
   715 
       
   716 				// Calculate pointer to pixel and the color
       
   717 				ptr = dpi->dst_ptr + y * dpi->pitch + x;
       
   718 				color = (type == 1) ? _vehicle_type_colors[v->type-0x10] : 0xF;
       
   719 
       
   720 				// And draw either one or two pixels depending on clipping
       
   721 				ptr[0] = color;
       
   722 				if (!skip) ptr[1] = color;
       
   723 			}
       
   724 		}
       
   725 	}
       
   726 
       
   727 	if (show_towns) {
       
   728 		const Town *t;
       
   729 
       
   730 		FOR_ALL_TOWNS(t) {
       
   731 			// Remap the town coordinate
       
   732 			Point pt = RemapCoords(
       
   733 				(int)(TileX(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_x) / TILE_SIZE,
       
   734 				(int)(TileY(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_y) / TILE_SIZE,
       
   735 				0);
       
   736 			x = pt.x - WP(w,smallmap_d).subscroll + 3 - (t->sign.width_2 >> 1);
       
   737 			y = pt.y;
       
   738 
       
   739 			// Check if the town sign is within bounds
       
   740 			if (x + t->sign.width_2 > dpi->left &&
       
   741 					x < dpi->left + dpi->width &&
       
   742 					y + 6 > dpi->top &&
       
   743 					y < dpi->top + dpi->height) {
       
   744 				// And draw it.
       
   745 				SetDParam(0, t->index);
       
   746 				DrawString(x, y, STR_2056, 12);
       
   747 			}
       
   748 		}
       
   749 	}
       
   750 
       
   751 	// Draw map indicators
       
   752 	{
       
   753 		Point pt;
       
   754 
       
   755 		// Find main viewport.
       
   756 		vp = FindWindowById(WC_MAIN_WINDOW,0)->viewport;
       
   757 
       
   758 		pt = RemapCoords(WP(w, smallmap_d).scroll_x, WP(w, smallmap_d).scroll_y, 0);
       
   759 
       
   760 		x = vp->virtual_left - pt.x;
       
   761 		y = vp->virtual_top - pt.y;
       
   762 		x2 = (x + vp->virtual_width) / TILE_SIZE;
       
   763 		y2 = (y + vp->virtual_height) / TILE_SIZE;
       
   764 		x /= TILE_SIZE;
       
   765 		y /= TILE_SIZE;
       
   766 
       
   767 		x -= WP(w,smallmap_d).subscroll;
       
   768 		x2 -= WP(w,smallmap_d).subscroll;
       
   769 
       
   770 		DrawVertMapIndicator(x, y, x, y2);
       
   771 		DrawVertMapIndicator(x2, y, x2, y2);
       
   772 
       
   773 		DrawHorizMapIndicator(x, y, x2, y);
       
   774 		DrawHorizMapIndicator(x, y2, x2, y2);
       
   775 	}
       
   776 	_cur_dpi = old_dpi;
       
   777 }
       
   778 
       
   779 void SmallMapCenterOnCurrentPos(Window *w)
       
   780 {
       
   781 	int x, y;
       
   782 	ViewPort *vp;
       
   783 	vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
       
   784 
       
   785 	x  = ((vp->virtual_width  - (w->widget[4].right  - w->widget[4].left) * TILE_SIZE) / 2 + vp->virtual_left) / 4;
       
   786 	y  = ((vp->virtual_height - (w->widget[4].bottom - w->widget[4].top ) * TILE_SIZE) / 2 + vp->virtual_top ) / 2 - TILE_SIZE * 2;
       
   787 	WP(w, smallmap_d).scroll_x = (y - x) & ~0xF;
       
   788 	WP(w, smallmap_d).scroll_y = (x + y) & ~0xF;
       
   789 	SetWindowDirty(w);
       
   790 }
       
   791 
       
   792 static void SmallMapWindowProc(Window *w, WindowEvent *e)
       
   793 {
       
   794 	switch (e->event) {
       
   795 		case WE_PAINT: {
       
   796 			const uint16 *tbl;
       
   797 			int x, y, y_org;
       
   798 			DrawPixelInfo new_dpi;
       
   799 
       
   800 			/* draw the window */
       
   801 			SetDParam(0, STR_00E5_CONTOURS + _smallmap_type);
       
   802 			DrawWindowWidgets(w);
       
   803 
       
   804 			/* draw the legend */
       
   805 			tbl = _legend_table[(_smallmap_type != 2) ? _smallmap_type : (_opt.landscape + IND_OFFS)];
       
   806 			x = 4;
       
   807 			y_org = w->height - 44 - 11;
       
   808 			y = y_org;
       
   809 			for (;;) {
       
   810 				GfxFillRect(x,     y + 1, x + 8, y + 5, 0);
       
   811 				GfxFillRect(x + 1, y + 2, x + 7, y + 4, (byte)tbl[0]);
       
   812 				DrawString(x + 11, y, tbl[1], 0);
       
   813 
       
   814 				tbl += 2;
       
   815 				y += 6;
       
   816 
       
   817 				if (tbl[0] == 0xFFFF) {
       
   818 					break;
       
   819 				} else if (tbl[0] & 0x100) {
       
   820 					x += 123;
       
   821 					y = y_org;
       
   822 				}
       
   823 			}
       
   824 
       
   825 			if (!FillDrawPixelInfo(&new_dpi, 3, 17, w->width - 28 + 22, w->height - 64 - 11))
       
   826 				return;
       
   827 
       
   828 			DrawSmallMap(&new_dpi, w, _smallmap_type, _smallmap_show_towns);
       
   829 		} break;
       
   830 
       
   831 		case WE_CLICK:
       
   832 			switch (e->we.click.widget) {
       
   833 				case 4: { // Map window
       
   834 					Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
       
   835 					Point pt;
       
   836 
       
   837 					/*
       
   838 					 * XXX: scrolling with the left mouse button is done by subsequently
       
   839 					 * clicking with the left mouse button; clicking once centers the
       
   840 					 * large map at the selected point. So by unclicking the left mouse
       
   841 					 * button here, it gets reclicked during the next inputloop, which
       
   842 					 * would make it look like the mouse is being dragged, while it is
       
   843 					 * actually being (virtually) clicked every inputloop.
       
   844 					 */
       
   845 					_left_button_clicked = false;
       
   846 
       
   847 					pt = RemapCoords(WP(w,smallmap_d).scroll_x, WP(w,smallmap_d).scroll_y, 0);
       
   848 					WP(w2, vp_d).scrollpos_x = pt.x + ((_cursor.pos.x - w->left + 2) << 4) - (w2->viewport->virtual_width >> 1);
       
   849 					WP(w2, vp_d).scrollpos_y = pt.y + ((_cursor.pos.y - w->top - 16) << 4) - (w2->viewport->virtual_height >> 1);
       
   850 
       
   851 					SetWindowDirty(w);
       
   852 				} break;
       
   853 
       
   854 				case 5:  // Show land contours
       
   855 				case 6:  // Show vehicles
       
   856 				case 7:  // Show industries
       
   857 				case 8:  // Show transport routes
       
   858 				case 9:  // Show vegetation
       
   859 				case 10: // Show land owners
       
   860 					RaiseWindowWidget(w, _smallmap_type + 5);
       
   861 					_smallmap_type = e->we.click.widget - 5;
       
   862 					LowerWindowWidget(w, _smallmap_type + 5);
       
   863 
       
   864 					SetWindowDirty(w);
       
   865 					SndPlayFx(SND_15_BEEP);
       
   866 					break;
       
   867 
       
   868 				case 11: // Center the smallmap again
       
   869 					SmallMapCenterOnCurrentPos(w);
       
   870 
       
   871 					SetWindowDirty(w);
       
   872 					SndPlayFx(SND_15_BEEP);
       
   873 					break;
       
   874 
       
   875 				case 12: // Toggle town names
       
   876 					ToggleWidgetLoweredState(w, 12);
       
   877 					_smallmap_show_towns = IsWindowWidgetLowered(w, 12);
       
   878 
       
   879 					SetWindowDirty(w);
       
   880 					SndPlayFx(SND_15_BEEP);
       
   881 					break;
       
   882 				}
       
   883 			break;
       
   884 
       
   885 		case WE_RCLICK:
       
   886 			if (e->we.click.widget == 4) {
       
   887 				if (_scrolling_viewport) return;
       
   888 				_scrolling_viewport = true;
       
   889 				_cursor.delta.x = 0;
       
   890 				_cursor.delta.y = 0;
       
   891 			}
       
   892 			break;
       
   893 
       
   894 		case WE_MOUSELOOP:
       
   895 			/* update the window every now and then */
       
   896 			if ((++w->vscroll.pos & 0x1F) == 0) SetWindowDirty(w);
       
   897 			break;
       
   898 
       
   899 		case WE_SCROLL: {
       
   900 			int x;
       
   901 			int y;
       
   902 			int sub;
       
   903 			int hx;
       
   904 			int hy;
       
   905 			int hvx;
       
   906 			int hvy;
       
   907 
       
   908 			_cursor.fix_at = true;
       
   909 
       
   910 			x = WP(w, smallmap_d).scroll_x;
       
   911 			y = WP(w, smallmap_d).scroll_y;
       
   912 
       
   913 			sub = WP(w, smallmap_d).subscroll + e->we.scroll.delta.x;
       
   914 
       
   915 			x -= (sub >> 2) << 4;
       
   916 			y += (sub >> 2) << 4;
       
   917 			sub &= 3;
       
   918 
       
   919 			x += (e->we.scroll.delta.y >> 1) << 4;
       
   920 			y += (e->we.scroll.delta.y >> 1) << 4;
       
   921 
       
   922 			if (e->we.scroll.delta.y & 1) {
       
   923 				x += TILE_SIZE;
       
   924 				sub += 2;
       
   925 				if (sub > 3) {
       
   926 					sub -= 4;
       
   927 					x -= TILE_SIZE;
       
   928 					y += TILE_SIZE;
       
   929 				}
       
   930 			}
       
   931 
       
   932 			hx = (w->widget[4].right  - w->widget[4].left) / 2;
       
   933 			hy = (w->widget[4].bottom - w->widget[4].top ) / 2;
       
   934 			hvx = hx * -4 + hy * 8;
       
   935 			hvy = hx *  4 + hy * 8;
       
   936 			if (x < -hvx) {
       
   937 				x = -hvx;
       
   938 				sub = 0;
       
   939 			}
       
   940 			if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
       
   941 				x = MapMaxX() * TILE_SIZE - hvx;
       
   942 				sub = 0;
       
   943 			}
       
   944 			if (y < -hvy) {
       
   945 				y = -hvy;
       
   946 				sub = 0;
       
   947 			}
       
   948 			if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
       
   949 				y = MapMaxY() * TILE_SIZE - hvy;
       
   950 				sub = 0;
       
   951 			}
       
   952 
       
   953 			WP(w, smallmap_d).scroll_x = x;
       
   954 			WP(w, smallmap_d).scroll_y = y;
       
   955 			WP(w, smallmap_d).subscroll = sub;
       
   956 
       
   957 			SetWindowDirty(w);
       
   958 		} break;
       
   959 	}
       
   960 }
       
   961 
       
   962 static const WindowDesc _smallmap_desc = {
       
   963 	WDP_AUTO, WDP_AUTO, 446, 314,
       
   964 	WC_SMALLMAP,0,
       
   965 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
       
   966 	_smallmap_widgets,
       
   967 	SmallMapWindowProc
       
   968 };
       
   969 
       
   970 void ShowSmallMap(void)
       
   971 {
       
   972 	Window *w;
       
   973 
       
   974 	w = AllocateWindowDescFront(&_smallmap_desc, 0);
       
   975 	if (w == NULL) return;
       
   976 
       
   977 	LowerWindowWidget(w, _smallmap_type + 5);
       
   978 	SetWindowWidgetLoweredState(w, 12, _smallmap_show_towns);
       
   979 	w->resize.width = 350;
       
   980 	w->resize.height = 250;
       
   981 
       
   982 	SmallMapCenterOnCurrentPos(w);
       
   983 }
       
   984 
       
   985 /* Extra ViewPort Window Stuff */
       
   986 static const Widget _extra_view_port_widgets[] = {
       
   987 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
       
   988 {    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   287,     0,    13, STR_EXTRA_VIEW_PORT_TITLE,        STR_018C_WINDOW_TITLE_DRAG_THIS},
       
   989 {  WWT_STICKYBOX,     RESIZE_LR,    14,   288,   299,     0,    13, 0x0,                              STR_STICKY_BUTTON},
       
   990 {      WWT_PANEL,     RESIZE_RB,    14,     0,   299,    14,   233, 0x0,                              STR_NULL},
       
   991 {      WWT_INSET,     RESIZE_RB,    14,     2,   297,    16,   231, 0x0,                              STR_NULL},
       
   992 { WWT_PUSHIMGBTN,     RESIZE_TB,    14,     0,    21,   234,   255, SPR_IMG_ZOOMIN,                   STR_017F_ZOOM_THE_VIEW_IN},
       
   993 { WWT_PUSHIMGBTN,     RESIZE_TB,    14,    22,    43,   234,   255, SPR_IMG_ZOOMOUT,                  STR_0180_ZOOM_THE_VIEW_OUT},
       
   994 { WWT_PUSHTXTBTN,     RESIZE_TB,    14,    44,   171,   234,   255, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT},
       
   995 { WWT_PUSHTXTBTN,     RESIZE_TB,    14,   172,   298,   234,   255, STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN, STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT},
       
   996 {      WWT_PANEL,    RESIZE_RTB,    14,   299,   299,   234,   255, 0x0,                              STR_NULL},
       
   997 {      WWT_PANEL,    RESIZE_RTB,    14,     0,   287,   256,   267, 0x0,                              STR_NULL},
       
   998 {  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   288,   299,   256,   267, 0x0,                              STR_RESIZE_BUTTON},
       
   999 {   WIDGETS_END},
       
  1000 };
       
  1001 
       
  1002 static void ExtraViewPortWndProc(Window *w, WindowEvent *e)
       
  1003 {
       
  1004 	switch (e->event) {
       
  1005 	case WE_CREATE: /* Disable zoom in button */
       
  1006 		DisableWindowWidget(w, 5);
       
  1007 		break;
       
  1008 
       
  1009 	case WE_PAINT:
       
  1010 		// set the number in the title bar
       
  1011 		SetDParam(0, w->window_number + 1);
       
  1012 
       
  1013 		DrawWindowWidgets(w);
       
  1014 		DrawWindowViewport(w);
       
  1015 		break;
       
  1016 
       
  1017 	case WE_CLICK:
       
  1018 		switch (e->we.click.widget) {
       
  1019 			case 5: DoZoomInOutWindow(ZOOM_IN,  w); break;
       
  1020 			case 6: DoZoomInOutWindow(ZOOM_OUT, w); break;
       
  1021 
       
  1022 		case 7: { /* location button (move main view to same spot as this view) 'Paste Location' */
       
  1023 			Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
       
  1024 			int x = WP(w, vp_d).scrollpos_x; // Where is the main looking at
       
  1025 			int y = WP(w, vp_d).scrollpos_y;
       
  1026 
       
  1027 			// set this view to same location. Based on the center, adjusting for zoom
       
  1028 			WP(w2, vp_d).scrollpos_x =  x - (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
       
  1029 			WP(w2, vp_d).scrollpos_y =  y - (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
       
  1030 		} break;
       
  1031 
       
  1032 		case 8: { /* inverse location button (move this view to same spot as main view) 'Copy Location' */
       
  1033 			const Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
       
  1034 			int x = WP(w2, const vp_d).scrollpos_x;
       
  1035 			int y = WP(w2, const vp_d).scrollpos_y;
       
  1036 
       
  1037 			WP(w, vp_d).scrollpos_x =  x + (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
       
  1038 			WP(w, vp_d).scrollpos_y =  y + (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
       
  1039 		} break;
       
  1040 		}
       
  1041 		break;
       
  1042 
       
  1043 	case WE_RESIZE:
       
  1044 		w->viewport->width          += e->we.sizing.diff.x;
       
  1045 		w->viewport->height         += e->we.sizing.diff.y;
       
  1046 		w->viewport->virtual_width  += e->we.sizing.diff.x;
       
  1047 		w->viewport->virtual_height += e->we.sizing.diff.y;
       
  1048 		break;
       
  1049 
       
  1050 		case WE_SCROLL: {
       
  1051 			ViewPort *vp = IsPtInWindowViewport(w, _cursor.pos.x, _cursor.pos.y);
       
  1052 
       
  1053 			if (vp == NULL) {
       
  1054 				_cursor.fix_at = false;
       
  1055 				_scrolling_viewport = false;
       
  1056 			}
       
  1057 
       
  1058 			WP(w, vp_d).scrollpos_x += e->we.scroll.delta.x << vp->zoom;
       
  1059 			WP(w, vp_d).scrollpos_y += e->we.scroll.delta.y << vp->zoom;
       
  1060 		} break;
       
  1061 
       
  1062 		case WE_MOUSEWHEEL:
       
  1063 			ZoomInOrOutToCursorWindow(e->we.wheel.wheel < 0, w);
       
  1064 			break;
       
  1065 
       
  1066 
       
  1067 		case WE_MESSAGE:
       
  1068 			/* Only handle zoom message if intended for us (msg ZOOM_IN/ZOOM_OUT) */
       
  1069 			if (e->we.message.wparam != w->window_number) break;
       
  1070 			HandleZoomMessage(w, w->viewport, 5, 6);
       
  1071 			break;
       
  1072 	}
       
  1073 }
       
  1074 
       
  1075 static const WindowDesc _extra_view_port_desc = {
       
  1076 	WDP_AUTO, WDP_AUTO, 300, 268,
       
  1077 	WC_EXTRA_VIEW_PORT,0,
       
  1078 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
       
  1079 	_extra_view_port_widgets,
       
  1080 	ExtraViewPortWndProc
       
  1081 };
       
  1082 
       
  1083 void ShowExtraViewPortWindow(void)
       
  1084 {
       
  1085 	Window *w, *v;
       
  1086 	int i = 0;
       
  1087 
       
  1088 	// find next free window number for extra viewport
       
  1089 	while (FindWindowById(WC_EXTRA_VIEW_PORT, i) != NULL) i++;
       
  1090 
       
  1091 	w = AllocateWindowDescFront(&_extra_view_port_desc, i);
       
  1092 	if (w != NULL) {
       
  1093 		int x, y;
       
  1094 		// the main window with the main view
       
  1095 		v = FindWindowById(WC_MAIN_WINDOW, 0);
       
  1096 		// New viewport start ats (zero,zero)
       
  1097 		AssignWindowViewport(w, 3, 17, 294, 214, 0 , 0);
       
  1098 
       
  1099 		// center on same place as main window (zoom is maximum, no adjustment needed)
       
  1100 		x = WP(v, vp_d).scrollpos_x;
       
  1101 		y = WP(v, vp_d).scrollpos_y;
       
  1102 		WP(w, vp_d).scrollpos_x = x + (v->viewport->virtual_width  - (294)) / 2;
       
  1103 		WP(w, vp_d).scrollpos_y = y + (v->viewport->virtual_height - (214)) / 2;
       
  1104 	}
       
  1105 }