src/viewport.cpp
branchNewGRF_ports
changeset 6719 4cc327ad39d5
parent 6718 5a8b295aa345
child 6720 35756db7e577
--- a/src/viewport.cpp	Tue Mar 27 23:27:27 2007 +0000
+++ b/src/viewport.cpp	Sat Jun 02 19:59:29 2007 +0000
@@ -11,6 +11,7 @@
 #include "strings.h"
 #include "table/sprites.h"
 #include "table/strings.h"
+#include "landscape.h"
 #include "map.h"
 #include "viewport.h"
 #include "window.h"
@@ -26,7 +27,9 @@
 
 #define VIEWPORT_DRAW_MEM (65536 * 2)
 
-// XXX - maximum viewports is maximum windows - 2 (main toolbar + status bar)
+ZoomLevel _saved_scrollpos_zoom;
+
+/* XXX - maximum viewports is maximum windows - 2 (main toolbar + status bar) */
 static ViewPort _viewports[25 - 2];
 static uint32 _active_viewports;    ///< bitmasked variable where each bit signifies if a viewport is in use or not
 assert_compile(lengthof(_viewports) < sizeof(_active_viewports) * 8);
@@ -92,8 +95,8 @@
 	byte zmax;
 };
 
-// Quick hack to know how much memory to reserve when allocating from the spritelist
-// to prevent a buffer overflow.
+/* Quick hack to know how much memory to reserve when allocating from the spritelist
+ * to prevent a buffer overflow. */
 #define LARGEST_SPRITELIST_STRUCT ParentSpriteToDraw
 
 struct ViewportDrawer {
@@ -143,7 +146,7 @@
 }
 
 void AssignWindowViewport(Window *w, int x, int y,
-	int width, int height, uint32 follow_flags, byte zoom)
+	int width, int height, uint32 follow_flags, ZoomLevel zoom)
 {
 	ViewPort *vp;
 	Point pt;
@@ -162,8 +165,8 @@
 
 	vp->zoom = zoom;
 
-	vp->virtual_width = width << zoom;
-	vp->virtual_height = height << zoom;
+	vp->virtual_width = ScaleByZoom(width, zoom);
+	vp->virtual_height = ScaleByZoom(height, zoom);
 
 	if (follow_flags & 0x80000000) {
 		const Vehicle *veh;
@@ -181,6 +184,9 @@
 
 	WP(w, vp_d).scrollpos_x = pt.x;
 	WP(w, vp_d).scrollpos_y = pt.y;
+	WP(w, vp_d).dest_scrollpos_x = pt.x;
+	WP(w, vp_d).dest_scrollpos_y = pt.y;
+
 	w->viewport = vp;
 	vp->virtual_left = 0;//pt.x;
 	vp->virtual_top = 0;//pt.y;
@@ -267,10 +273,10 @@
 	vp->virtual_left = x;
 	vp->virtual_top = y;
 
-	old_left >>= vp->zoom;
-	old_top >>= vp->zoom;
-	x >>= vp->zoom;
-	y >>= vp->zoom;
+	old_left = UnScaleByZoom(old_left, vp->zoom);
+	old_top = UnScaleByZoom(old_top, vp->zoom);
+	x = UnScaleByZoom(x, vp->zoom);
+	y = UnScaleByZoom(y, vp->zoom);
 
 	old_left -= x;
 	old_top -= y;
@@ -331,8 +337,8 @@
 				return pt;
 	}
 
-	x = ((x << vp->zoom) + vp->virtual_left) >> 2;
-	y = ((y << vp->zoom) + vp->virtual_top) >> 1;
+	x = (ScaleByZoom(x, vp->zoom) + vp->virtual_left) >> 2;
+	y = (ScaleByZoom(y, vp->zoom) + vp->virtual_top) >> 1;
 
 	a = y-x;
 	b = y+x;
@@ -398,16 +404,16 @@
 
 /** Update the status of the zoom-buttons according to the zoom-level
  * of the viewport. This will update their status and invalidate accordingly
- * @param window pointer to the window that has the zoom buttons
+ * @param w Window pointer to the window that has the zoom buttons
  * @param vp pointer to the viewport whose zoom-level the buttons represent
  * @param widget_zoom_in widget index for window with zoom-in button
  * @param widget_zoom_out widget index for window with zoom-out button */
 void HandleZoomMessage(Window *w, const ViewPort *vp, byte widget_zoom_in, byte widget_zoom_out)
 {
-	SetWindowWidgetDisabledState(w, widget_zoom_in, vp->zoom == 0);
+	SetWindowWidgetDisabledState(w, widget_zoom_in, vp->zoom == ZOOM_LVL_MIN);
 	InvalidateWidget(w, widget_zoom_in);
 
-	SetWindowWidgetDisabledState(w, widget_zoom_out, vp->zoom == 2);
+	SetWindowWidgetDisabledState(w, widget_zoom_out, vp->zoom == ZOOM_LVL_MAX);
 	InvalidateWidget(w, widget_zoom_out);
 }
 
@@ -439,7 +445,7 @@
 void DrawGroundSprite(SpriteID image, SpriteID pal)
 {
 	if (_offset_ground_sprites) {
-		// offset ground sprite because of foundation?
+		/* offset ground sprite because of foundation? */
 		AddChildSpriteScreen(image, pal, _cur_vd->offs_x, _cur_vd->offs_y);
 	} else {
 		_added_tile_sprite = true;
@@ -494,12 +500,12 @@
 	ps = (ParentSpriteToDraw*)vd->spritelist_mem;
 
 	if (vd->parent_list >= vd->eof_parent_list) {
-		// This can happen rarely, mostly when you zoom out completely
-		//  and have a lot of stuff that moves (and is added to the
-		//  sort-list, this function). To solve it, increase
-		//  parent_list somewhere below to a higher number.
-		// This can not really hurt you, it just gives some black
-		//  spots on the screen ;)
+		/* This can happen rarely, mostly when you zoom out completely
+		 *  and have a lot of stuff that moves (and is added to the
+		 *  sort-list, this function). To solve it, increase
+		 *  parent_list somewhere below to a higher number.
+		 * This can not really hurt you, it just gives some black
+		 *  spots on the screen ;) */
 		DEBUG(sprite, 0, "Out of sprite memory (parent_list)");
 		return;
 	}
@@ -647,16 +653,16 @@
 	SpriteID image;
 	SpriteID pal;
 
-	// Draw a red error square?
+	/* Draw a red error square? */
 	if (_thd.redsq != 0 && _thd.redsq == ti->tile) {
 		DrawSelectionSprite(SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh], PALETTE_TILE_RED_PULSATING, ti);
 		return;
 	}
 
-	// no selection active?
+	/* no selection active? */
 	if (_thd.drawstyle == 0) return;
 
-	// Inside the inner area?
+	/* Inside the inner area? */
 	if (IS_INSIDE_1D(ti->x, _thd.pos.x, _thd.size.x) &&
 			IS_INSIDE_1D(ti->y, _thd.pos.y, _thd.size.y)) {
 		if (_thd.drawstyle & HT_RECT) {
@@ -669,15 +675,15 @@
 				DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE, ti);
 			}
 		} else if (_thd.drawstyle & HT_POINT) {
-			// Figure out the Z coordinate for the single dot.
+			/* Figure out the Z coordinate for the single dot. */
 			byte z = ti->z;
 			if (ti->tileh & SLOPE_N) {
 				z += TILE_HEIGHT;
 				if (ti->tileh == SLOPE_STEEP_N) z += TILE_HEIGHT;
 			}
-			DrawGroundSpriteAt(_cur_dpi->zoom != 2 ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti->x, ti->y, z);
+			DrawGroundSpriteAt(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti->x, ti->y, z);
 		} else if (_thd.drawstyle & HT_RAIL /*&& _thd.place_mode == VHM_RAIL*/) {
-			// autorail highlight piece under cursor
+			/* autorail highlight piece under cursor */
 			uint type = _thd.drawstyle & 0xF;
 			int offset;
 
@@ -695,7 +701,7 @@
 			DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti);
 
 		} else if (IsPartOfAutoLine(ti->x, ti->y)) {
-			// autorail highlighting long line
+			/* autorail highlighting long line */
 			int dir = _thd.drawstyle & ~0xF0;
 			int offset;
 			uint side;
@@ -721,12 +727,12 @@
 		return;
 	}
 
-	// Check if it's inside the outer area?
+	/* Check if it's inside the outer area? */
 	if (_thd.outersize.x &&
 			_thd.size.x < _thd.size.x + _thd.outersize.x &&
 			IS_INSIDE_1D(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
 			IS_INSIDE_1D(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
-		// Draw a blue rect.
+		/* Draw a blue rect. */
 		DrawSelectionSprite(SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh], PALETTE_SEL_TILE_BLUE, ti);
 		return;
 	}
@@ -741,11 +747,11 @@
 
 	_cur_ti = &ti;
 
-	// Transform into tile coordinates and round to closest full tile
+	/* Transform into tile coordinates and round to closest full tile */
 	x = ((vd->dpi.top >> 1) - (vd->dpi.left >> 2)) & ~0xF;
 	y = ((vd->dpi.top >> 1) + (vd->dpi.left >> 2) - 0x10) & ~0xF;
 
-	// determine size of area
+	/* determine size of area */
 	{
 		Point pt = RemapCoords(x, y, 241);
 		width = (vd->dpi.left + vd->dpi.width - pt.x + 95) >> 6;
@@ -805,7 +811,7 @@
 	Town *t;
 	int left, top, right, bottom;
 
-	if (!(_display_opt & DO_SHOW_TOWN_NAMES) || _game_mode == GM_MENU)
+	if (!HASBIT(_display_opt, DO_SHOW_TOWN_NAMES) || _game_mode == GM_MENU)
 		return;
 
 	left = dpi->left;
@@ -814,7 +820,7 @@
 	bottom = top + dpi->height;
 
 	switch (dpi->zoom) {
-		case 0:
+		case ZOOM_LVL_NORMAL:
 			FOR_ALL_TOWNS(t) {
 				if (bottom > t->sign.top &&
 						top    < t->sign.top + 12 &&
@@ -827,7 +833,7 @@
 			}
 			break;
 
-		case 1:
+		case ZOOM_LVL_OUT_2X:
 			right += 2;
 			bottom += 2;
 
@@ -843,21 +849,26 @@
 			}
 			break;
 
-		default: NOT_REACHED();
-		case 2:
-			right += 4;
-			bottom += 5;
+		case ZOOM_LVL_OUT_4X:
+		case ZOOM_LVL_OUT_8X:
+			right += ScaleByZoom(1, dpi->zoom);
+			bottom += ScaleByZoom(1, dpi->zoom) + 1;
 
 			FOR_ALL_TOWNS(t) {
 				if (bottom > t->sign.top &&
-						top    < t->sign.top + 24 &&
+						top    < t->sign.top + ScaleByZoom(12, dpi->zoom) &&
 						right  > t->sign.left &&
-						left   < t->sign.left + t->sign.width_2*4) {
+						left   < t->sign.left + ScaleByZoom(t->sign.width_2, dpi->zoom)) {
 					AddStringToDraw(t->sign.left + 5, t->sign.top + 1, STR_TOWN_LABEL_TINY_BLACK, t->index, 0);
 					AddStringToDraw(t->sign.left + 1, t->sign.top - 3, STR_TOWN_LABEL_TINY_WHITE, t->index, 0);
 				}
 			}
 			break;
+
+		case ZOOM_LVL_OUT_16X:
+			break;
+
+		default: NOT_REACHED();
 	}
 }
 
@@ -879,7 +890,7 @@
 	int left, top, right, bottom;
 	const Station *st;
 
-	if (!(_display_opt & DO_SHOW_STATION_NAMES) || _game_mode == GM_MENU)
+	if (!HASBIT(_display_opt, DO_SHOW_STATION_NAMES) || _game_mode == GM_MENU)
 		return;
 
 	left = dpi->left;
@@ -888,7 +899,7 @@
 	bottom = top + dpi->height;
 
 	switch (dpi->zoom) {
-		case 0:
+		case ZOOM_LVL_NORMAL:
 			FOR_ALL_STATIONS(st) {
 				if (bottom > st->sign.top &&
 						top    < st->sign.top + 12 &&
@@ -899,7 +910,7 @@
 			}
 			break;
 
-		case 1:
+		case ZOOM_LVL_OUT_2X:
 			right += 2;
 			bottom += 2;
 			FOR_ALL_STATIONS(st) {
@@ -912,19 +923,25 @@
 			}
 			break;
 
-		default: NOT_REACHED();
-		case 2:
-			right += 4;
-			bottom += 5;
+		case ZOOM_LVL_OUT_4X:
+		case ZOOM_LVL_OUT_8X:
+			right += ScaleByZoom(1, dpi->zoom);
+			bottom += ScaleByZoom(1, dpi->zoom) + 1;
+
 			FOR_ALL_STATIONS(st) {
 				if (bottom > st->sign.top &&
-						top    < st->sign.top + 24 &&
+						top    < st->sign.top + ScaleByZoom(12, dpi->zoom) &&
 						right  > st->sign.left &&
-						left   < st->sign.left + st->sign.width_2*4) {
+						left   < st->sign.left + ScaleByZoom(st->sign.width_2, dpi->zoom)) {
 					AddStation(st, STR_STATION_SIGN_TINY, st->sign.width_2 | 0x8000);
 				}
 			}
 			break;
+
+		case ZOOM_LVL_OUT_16X:
+			break;
+
+		default: NOT_REACHED();
 	}
 }
 
@@ -946,7 +963,7 @@
 	const Sign *si;
 	int left, top, right, bottom;
 
-	if (!(_display_opt & DO_SHOW_SIGNS))
+	if (!HASBIT(_display_opt, DO_SHOW_SIGNS))
 		return;
 
 	left = dpi->left;
@@ -955,7 +972,7 @@
 	bottom = top + dpi->height;
 
 	switch (dpi->zoom) {
-		case 0:
+		case ZOOM_LVL_NORMAL:
 			FOR_ALL_SIGNS(si) {
 				if (bottom > si->sign.top &&
 						top    < si->sign.top + 12 &&
@@ -966,7 +983,7 @@
 			}
 			break;
 
-		case 1:
+		case ZOOM_LVL_OUT_2X:
 			right += 2;
 			bottom += 2;
 			FOR_ALL_SIGNS(si) {
@@ -979,19 +996,25 @@
 			}
 			break;
 
-		default: NOT_REACHED();
-		case 2:
-			right += 4;
-			bottom += 5;
+		case ZOOM_LVL_OUT_4X:
+		case ZOOM_LVL_OUT_8X:
+			right += ScaleByZoom(1, dpi->zoom);
+			bottom += ScaleByZoom(1, dpi->zoom) + 1;
+
 			FOR_ALL_SIGNS(si) {
 				if (bottom > si->sign.top &&
-						top    < si->sign.top + 24 &&
+						top    < si->sign.top + ScaleByZoom(12, dpi->zoom) &&
 						right  > si->sign.left &&
-						left   < si->sign.left + si->sign.width_2 * 4) {
+						left   < si->sign.left + ScaleByZoom(si->sign.width_2, dpi->zoom)) {
 					AddSign(si, STR_2002, si->sign.width_2 | 0x8000);
 				}
 			}
 			break;
+
+		case ZOOM_LVL_OUT_16X:
+			break;
+
+		default: NOT_REACHED();
 	}
 }
 
@@ -1013,7 +1036,7 @@
 	const Waypoint *wp;
 	int left, top, right, bottom;
 
-	if (!(_display_opt & DO_WAYPOINTS))
+	if (!HASBIT(_display_opt, DO_WAYPOINTS))
 		return;
 
 	left = dpi->left;
@@ -1022,7 +1045,7 @@
 	bottom = top + dpi->height;
 
 	switch (dpi->zoom) {
-		case 0:
+		case ZOOM_LVL_NORMAL:
 			FOR_ALL_WAYPOINTS(wp) {
 				if (bottom > wp->sign.top &&
 						top    < wp->sign.top + 12 &&
@@ -1033,7 +1056,7 @@
 			}
 			break;
 
-		case 1:
+		case ZOOM_LVL_OUT_2X:
 			right += 2;
 			bottom += 2;
 			FOR_ALL_WAYPOINTS(wp) {
@@ -1046,19 +1069,25 @@
 			}
 			break;
 
-		default: NOT_REACHED();
-		case 2:
-			right += 4;
-			bottom += 5;
+		case ZOOM_LVL_OUT_4X:
+		case ZOOM_LVL_OUT_8X:
+			right += ScaleByZoom(1, dpi->zoom);
+			bottom += ScaleByZoom(1, dpi->zoom) + 1;
+
 			FOR_ALL_WAYPOINTS(wp) {
 				if (bottom > wp->sign.top &&
-						top    < wp->sign.top + 24 &&
+						top    < wp->sign.top + ScaleByZoom(12, dpi->zoom) &&
 						right  > wp->sign.left &&
-						left   < wp->sign.left + wp->sign.width_2*4) {
+						left   < wp->sign.left + ScaleByZoom(wp->sign.width_2, dpi->zoom)) {
 					AddWaypoint(wp, STR_WAYPOINT_VIEWPORT_TINY, wp->sign.width_2 | 0x8000);
 				}
 			}
 			break;
+
+		case ZOOM_LVL_OUT_16X:
+			break;
+
+		default: NOT_REACHED();
 	}
 }
 
@@ -1135,7 +1164,7 @@
 					}
 				}
 
-				// Swap the two sprites ps and ps2 using bubble-sort algorithm.
+				/* Swap the two sprites ps and ps2 using bubble-sort algorithm. */
 				psd3 = psd;
 				do {
 					ParentSpriteToDraw* temp = *psd3;
@@ -1169,25 +1198,25 @@
 static void ViewportDrawStrings(DrawPixelInfo *dpi, const StringSpriteToDraw *ss)
 {
 	DrawPixelInfo dp;
-	byte zoom;
+	ZoomLevel zoom;
 
 	_cur_dpi = &dp;
 	dp = *dpi;
 
 	zoom = dp.zoom;
-	dp.zoom = 0;
-
-	dp.left >>= zoom;
-	dp.top >>= zoom;
-	dp.width >>= zoom;
-	dp.height >>= zoom;
+	dp.zoom = ZOOM_LVL_NORMAL;
+
+	dp.left   = UnScaleByZoom(dp.left,   zoom);
+	dp.top    = UnScaleByZoom(dp.top,    zoom);
+	dp.width  = UnScaleByZoom(dp.width,  zoom);
+	dp.height = UnScaleByZoom(dp.height, zoom);
 
 	do {
 		uint16 colour;
 
 		if (ss->width != 0) {
-			int x = (ss->x >> zoom) - 1;
-			int y = (ss->y >> zoom) - 1;
+			int x = UnScaleByZoom(ss->x, zoom) - 1;
+			int y = UnScaleByZoom(ss->y, zoom) - 1;
 			int bottom = y + 11;
 			int w = ss->width;
 
@@ -1200,21 +1229,19 @@
 
 		/* Draw the rectangle if 'tranparent station signs' is off,
 		 * or if we are drawing a general text sign (STR_2806) */
-			if (!(_display_opt & DO_TRANS_SIGNS) || ss->string == STR_2806)
+			if (!HASBIT(_transparent_opt, TO_SIGNS) || ss->string == STR_2806) {
 				DrawFrameRect(
 					x, y, x + w, bottom, ss->color,
-					(_display_opt & DO_TRANS_BUILDINGS) ? FR_TRANSPARENT : FR_NONE
+					HASBIT(_transparent_opt, TO_SIGNS) ? FR_TRANSPARENT : FR_NONE
 				);
+			}
 		}
 
 		SetDParam(0, ss->params[0]);
 		SetDParam(1, ss->params[1]);
 		/* if we didn't draw a rectangle, or if transparant building is on,
 		 * draw the text in the color the rectangle would have */
-		if ((
-					(_display_opt & DO_TRANS_BUILDINGS) ||
-					(_display_opt & DO_TRANS_SIGNS && ss->string != STR_2806)
-				) && ss->width != 0) {
+		if (HASBIT(_transparent_opt, TO_SIGNS) && ss->string != STR_2806 && ss->width != 0) {
 			/* Real colors need the IS_PALETTE_COLOR flag
 			 * otherwise colors from _string_colormap are assumed. */
 			colour = _colour_gradient[ss->color][6] | IS_PALETTE_COLOR;
@@ -1222,7 +1249,7 @@
 			colour = 16;
 		}
 		DrawString(
-			ss->x >> zoom, (ss->y >> zoom) - (ss->width & 0x8000 ? 2 : 0),
+			UnScaleByZoom(ss->x, zoom), UnScaleByZoom(ss->y, zoom) - (ss->width & 0x8000 ? 2 : 0),
 			ss->string, colour
 		);
 
@@ -1247,7 +1274,7 @@
 	_cur_dpi = &vd.dpi;
 
 	vd.dpi.zoom = vp->zoom;
-	mask = (-1) << vp->zoom;
+	mask = ScaleByZoom(-1, vp->zoom);
 
 	vd.combine_sprites = 0;
 
@@ -1257,8 +1284,8 @@
 	vd.dpi.top = top & mask;
 	vd.dpi.pitch = old_dpi->pitch;
 
-	x = ((vd.dpi.left - (vp->virtual_left&mask)) >> vp->zoom) + vp->left;
-	y = ((vd.dpi.top - (vp->virtual_top&mask)) >> vp->zoom) + vp->top;
+	x = UnScaleByZoom(vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
+	y = UnScaleByZoom(vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
 
 	vd.dpi.dst_ptr = old_dpi->dst_ptr + x - old_dpi->left + (y - old_dpi->top) * old_dpi->pitch;
 
@@ -1280,8 +1307,8 @@
 	ViewportAddSigns(&vd.dpi);
 	ViewportAddWaypoints(&vd.dpi);
 
-	// This assert should never happen (because the length of the parent_list
-	//  is checked)
+	/* This assert should never happen (because the length of the parent_list
+	 *  is checked) */
 	assert(vd.parent_list <= endof(parent_list));
 
 	if (vd.first_tile != NULL) ViewportDrawTileSprites(vd.first_tile);
@@ -1297,11 +1324,11 @@
 	_cur_dpi = old_dpi;
 }
 
-// Make sure we don't draw a too big area at a time.
-// If we do, the sprite memory will overflow.
+/** Make sure we don't draw a too big area at a time.
+ * If we do, the sprite memory will overflow. */
 static void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, int bottom)
 {
-	if (((bottom - top) * (right - left) << (2 * vp->zoom)) > 180000) {
+	if (ScaleByZoom(bottom - top, vp->zoom) * ScaleByZoom(right - left, vp->zoom) > 180000) {
 		if ((bottom - top) > (right - left)) {
 			int t = (top + bottom) >> 1;
 			ViewportDrawChk(vp, left, top, right, t);
@@ -1313,10 +1340,10 @@
 		}
 	} else {
 		ViewportDoDraw(vp,
-			((left - vp->left) << vp->zoom) + vp->virtual_left,
-			((top - vp->top) << vp->zoom) + vp->virtual_top,
-			((right - vp->left) << vp->zoom) + vp->virtual_left,
-			((bottom - vp->top) << vp->zoom) + vp->virtual_top
+			ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
+			ScaleByZoom(top - vp->top, vp->zoom) + vp->virtual_top,
+			ScaleByZoom(right - vp->left, vp->zoom) + vp->virtual_left,
+			ScaleByZoom(bottom - vp->top, vp->zoom) + vp->virtual_top
 		);
 	}
 }
@@ -1366,20 +1393,39 @@
 		int vx;
 		int vy;
 
-		// Center of the viewport is hot spot
+		/* Center of the viewport is hot spot */
 		x = WP(w,vp_d).scrollpos_x + vp->virtual_width / 2;
 		y = WP(w,vp_d).scrollpos_y + vp->virtual_height / 2;
-		// Convert viewport coordinates to map coordinates
-		// Calculation is scaled by 4 to avoid rounding errors
+
+		int dest_x = WP(w,vp_d).dest_scrollpos_x + vp->virtual_width / 2;
+		int dest_y = WP(w,vp_d).dest_scrollpos_y + vp->virtual_height / 2;
+
+		int delta_x = dest_x - x;
+		int delta_y = dest_y - y;
+
+		if (delta_x != 0 || delta_y != 0) {
+			if (_patches.smooth_scroll) {
+				int max_scroll = ScaleByMapSize1D(512);
+				/* Not at our desired positon yet... */
+				x += clamp(delta_x / 8, -max_scroll, max_scroll);
+				y += clamp(delta_y / 8, -max_scroll, max_scroll);
+			} else {
+				x = dest_x;
+				y = dest_y;
+			}
+		}
+
+		/* Convert viewport coordinates to map coordinates
+		 * Calculation is scaled by 4 to avoid rounding errors */
 		vx = -x + y * 2;
 		vy =  x + y * 2;
-		// clamp to size of map
+		/* clamp to size of map */
 		vx = clamp(vx, 0 * 4, MapMaxX() * TILE_SIZE * 4);
 		vy = clamp(vy, 0 * 4, MapMaxY() * TILE_SIZE * 4);
-		// Convert map coordinates to viewport coordinates
+		/* Convert map coordinates to viewport coordinates */
 		x = (-vx + vy) / 2;
 		y = ( vx + vy) / 4;
-		// Set position
+		/* Set position */
 		WP(w, vp_d).scrollpos_x = x - vp->virtual_width / 2;
 		WP(w, vp_d).scrollpos_y = y - vp->virtual_height / 2;
 
@@ -1404,10 +1450,10 @@
 	if (top >= vp->virtual_height) return;
 
 	SetDirtyBlocks(
-		(left >> vp->zoom) + vp->left,
-		(top >> vp->zoom) + vp->top,
-		(right >> vp->zoom) + vp->left,
-		(bottom >> vp->zoom) + vp->top
+		UnScaleByZoom(left, vp->zoom) + vp->left,
+		UnScaleByZoom(top, vp->zoom) + vp->top,
+		UnScaleByZoom(right, vp->zoom) + vp->left,
+		UnScaleByZoom(bottom, vp->zoom) + vp->top
 	);
 }
 
@@ -1495,10 +1541,10 @@
 {
 	const Town *t;
 
-	if (!(_display_opt & DO_SHOW_TOWN_NAMES)) return false;
+	if (!HASBIT(_display_opt, DO_SHOW_TOWN_NAMES)) return false;
 
 	switch (vp->zoom) {
-		case 0:
+		case ZOOM_LVL_NORMAL:
 			x = x - vp->left + vp->virtual_left;
 			y = y - vp->top  + vp->virtual_top;
 			FOR_ALL_TOWNS(t) {
@@ -1512,7 +1558,7 @@
 			}
 			break;
 
-		case 1:
+		case ZOOM_LVL_OUT_2X:
 			x = (x - vp->left + 1) * 2 + vp->virtual_left;
 			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
 			FOR_ALL_TOWNS(t) {
@@ -1526,19 +1572,26 @@
 			}
 			break;
 
-		default:
-			x = (x - vp->left + 3) * 4 + vp->virtual_left;
-			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
+		case ZOOM_LVL_OUT_4X:
+		case ZOOM_LVL_OUT_8X:
+			x = ScaleByZoom(x - vp->left + ScaleByZoom(1, vp->zoom) - 1, vp->zoom) + vp->virtual_left;
+			y = ScaleByZoom(y - vp->top  + ScaleByZoom(1, vp->zoom) - 1, vp->zoom) + vp->virtual_top;
+
 			FOR_ALL_TOWNS(t) {
 				if (y >= t->sign.top &&
-						y < t->sign.top + 24 &&
+						y < t->sign.top + ScaleByZoom(12, vp->zoom) &&
 						x >= t->sign.left &&
-						x < t->sign.left + t->sign.width_2 * 4) {
+						x < t->sign.left + ScaleByZoom(t->sign.width_2, vp->zoom)) {
 					ShowTownViewWindow(t->index);
 					return true;
 				}
 			}
 			break;
+
+		case ZOOM_LVL_OUT_16X:
+			break;
+
+		default: NOT_REACHED();
 	}
 
 	return false;
@@ -1549,10 +1602,10 @@
 {
 	const Station *st;
 
-	if (!(_display_opt & DO_SHOW_STATION_NAMES)) return false;
+	if (!HASBIT(_display_opt, DO_SHOW_STATION_NAMES)) return false;
 
 	switch (vp->zoom) {
-		case 0:
+		case ZOOM_LVL_NORMAL:
 			x = x - vp->left + vp->virtual_left;
 			y = y - vp->top  + vp->virtual_top;
 			FOR_ALL_STATIONS(st) {
@@ -1566,7 +1619,7 @@
 			}
 			break;
 
-		case 1:
+		case ZOOM_LVL_OUT_2X:
 			x = (x - vp->left + 1) * 2 + vp->virtual_left;
 			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
 			FOR_ALL_STATIONS(st) {
@@ -1580,19 +1633,26 @@
 			}
 			break;
 
-		default:
-			x = (x - vp->left + 3) * 4 + vp->virtual_left;
-			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
+		case ZOOM_LVL_OUT_4X:
+		case ZOOM_LVL_OUT_8X:
+			x = ScaleByZoom(x - vp->left + ScaleByZoom(1, vp->zoom) - 1, vp->zoom) + vp->virtual_left;
+			y = ScaleByZoom(y - vp->top  + ScaleByZoom(1, vp->zoom) - 1, vp->zoom) + vp->virtual_top;
+
 			FOR_ALL_STATIONS(st) {
 				if (y >= st->sign.top &&
-						y < st->sign.top + 24 &&
+						y < st->sign.top + ScaleByZoom(12, vp->zoom) &&
 						x >= st->sign.left &&
-						x < st->sign.left + st->sign.width_2 * 4) {
+						x < st->sign.left + ScaleByZoom(st->sign.width_2, vp->zoom)) {
 					ShowStationViewWindow(st->index);
 					return true;
 				}
 			}
 			break;
+
+		case ZOOM_LVL_OUT_16X:
+			break;
+
+		default: NOT_REACHED();
 	}
 
 	return false;
@@ -1603,10 +1663,10 @@
 {
 	const Sign *si;
 
-	if (!(_display_opt & DO_SHOW_SIGNS) || _current_player == PLAYER_SPECTATOR) return false;
+	if (!HASBIT(_display_opt, DO_SHOW_SIGNS) || _current_player == PLAYER_SPECTATOR) return false;
 
 	switch (vp->zoom) {
-		case 0:
+		case ZOOM_LVL_NORMAL:
 			x = x - vp->left + vp->virtual_left;
 			y = y - vp->top  + vp->virtual_top;
 			FOR_ALL_SIGNS(si) {
@@ -1620,7 +1680,7 @@
 			}
 			break;
 
-		case 1:
+		case ZOOM_LVL_OUT_2X:
 			x = (x - vp->left + 1) * 2 + vp->virtual_left;
 			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
 			FOR_ALL_SIGNS(si) {
@@ -1634,19 +1694,26 @@
 			}
 			break;
 
-		default:
-			x = (x - vp->left + 3) * 4 + vp->virtual_left;
-			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
+		case ZOOM_LVL_OUT_4X:
+		case ZOOM_LVL_OUT_8X:
+			x = ScaleByZoom(x - vp->left + ScaleByZoom(1, vp->zoom) - 1, vp->zoom) + vp->virtual_left;
+			y = ScaleByZoom(y - vp->top  + ScaleByZoom(1, vp->zoom) - 1, vp->zoom) + vp->virtual_top;
+
 			FOR_ALL_SIGNS(si) {
 				if (y >= si->sign.top &&
-						y <  si->sign.top + 24 &&
+						y <  si->sign.top + ScaleByZoom(12, vp->zoom) &&
 						x >= si->sign.left &&
-						x <  si->sign.left + si->sign.width_2 * 4) {
+						x <  si->sign.left + ScaleByZoom(si->sign.width_2, vp->zoom)) {
 					ShowRenameSignWindow(si);
 					return true;
 				}
 			}
 			break;
+
+		case ZOOM_LVL_OUT_16X:
+			break;
+
+		default: NOT_REACHED();
 	}
 
 	return false;
@@ -1657,10 +1724,10 @@
 {
 	const Waypoint *wp;
 
-	if (!(_display_opt & DO_WAYPOINTS)) return false;
+	if (!HASBIT(_display_opt, DO_WAYPOINTS)) return false;
 
 	switch (vp->zoom) {
-		case 0:
+		case ZOOM_LVL_NORMAL:
 			x = x - vp->left + vp->virtual_left;
 			y = y - vp->top  + vp->virtual_top;
 			FOR_ALL_WAYPOINTS(wp) {
@@ -1674,7 +1741,7 @@
 			}
 			break;
 
-		case 1:
+		case ZOOM_LVL_OUT_2X:
 			x = (x - vp->left + 1) * 2 + vp->virtual_left;
 			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
 			FOR_ALL_WAYPOINTS(wp) {
@@ -1688,19 +1755,26 @@
 			}
 			break;
 
-		default:
-			x = (x - vp->left + 3) * 4 + vp->virtual_left;
-			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
+		case ZOOM_LVL_OUT_4X:
+		case ZOOM_LVL_OUT_8X:
+			x = ScaleByZoom(x - vp->left + ScaleByZoom(1, vp->zoom) - 1, vp->zoom) + vp->virtual_left;
+			y = ScaleByZoom(y - vp->top  + ScaleByZoom(1, vp->zoom) - 1, vp->zoom) + vp->virtual_top;
+
 			FOR_ALL_WAYPOINTS(wp) {
 				if (y >= wp->sign.top &&
-						y < wp->sign.top + 24 &&
+						y < wp->sign.top + ScaleByZoom(12, vp->zoom) &&
 						x >= wp->sign.left &&
-						x < wp->sign.left + wp->sign.width_2 * 4) {
+						x < wp->sign.left + ScaleByZoom(wp->sign.width_2, vp->zoom)) {
 					ShowRenameWaypointWindow(wp);
 					return true;
 				}
 			}
 			break;
+
+		case ZOOM_LVL_OUT_16X:
+			break;
+
+		default: NOT_REACHED();
 	}
 
 	return false;
@@ -1796,26 +1870,31 @@
 
 
 /* scrolls the viewport in a window to a given location */
-bool ScrollWindowTo(int x , int y, Window *w)
+bool ScrollWindowTo(int x , int y, Window *w, bool instant)
 {
 	Point pt;
 
 	pt = MapXYZToViewport(w->viewport, x, y, GetSlopeZ(x, y));
 	WP(w, vp_d).follow_vehicle = INVALID_VEHICLE;
 
-	if (WP(w, vp_d).scrollpos_x == pt.x && WP(w, vp_d).scrollpos_y == pt.y)
+	if (WP(w, vp_d).dest_scrollpos_x == pt.x && WP(w, vp_d).dest_scrollpos_y == pt.y)
 		return false;
 
-	WP(w, vp_d).scrollpos_x = pt.x;
-	WP(w, vp_d).scrollpos_y = pt.y;
+	if (instant) {
+		WP(w, vp_d).scrollpos_x = pt.x;
+		WP(w, vp_d).scrollpos_y = pt.y;
+	}
+
+	WP(w, vp_d).dest_scrollpos_x = pt.x;
+	WP(w, vp_d).dest_scrollpos_y = pt.y;
 	return true;
 }
 
 
-bool ScrollMainWindowTo(int x, int y)
+bool ScrollMainWindowTo(int x, int y, bool instant)
 {
 	Window *w;
-	bool res = ScrollWindowTo(x, y, FindWindowById(WC_MAIN_WINDOW, 0));
+	bool res = ScrollWindowTo(x, y, FindWindowById(WC_MAIN_WINDOW, 0), instant);
 
 	/* If a user scrolls to a tile (via what way what so ever) and already is on
 	 *  that tile (e.g.: pressed twice), move the smallmap to that location,
@@ -1832,9 +1911,9 @@
 }
 
 
-bool ScrollMainWindowToTile(TileIndex tile)
+bool ScrollMainWindowToTile(TileIndex tile, bool instant)
 {
-	return ScrollMainWindowTo(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2);
+	return ScrollMainWindowTo(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, instant);
 }
 
 void SetRedErrorSquare(TileIndex tile)
@@ -1866,13 +1945,13 @@
 	_thd.new_outersize.y = sy * TILE_SIZE;
 }
 
-/* returns the best autorail highlight type from map coordinates */
+/** returns the best autorail highlight type from map coordinates */
 static byte GetAutorailHT(int x, int y)
 {
 	return HT_RAIL | _AutorailPiece[x & 0xF][y & 0xF];
 }
 
-// called regular to update tile highlighting in all cases
+/** called regular to update tile highlighting in all cases */
 void UpdateTileSelection()
 {
 	int x1;
@@ -1919,13 +1998,13 @@
 		}
 	}
 
-	// redraw selection
+	/* redraw selection */
 	if (_thd.drawstyle != _thd.new_drawstyle ||
 			_thd.pos.x != _thd.new_pos.x || _thd.pos.y != _thd.new_pos.y ||
 			_thd.size.x != _thd.new_size.x || _thd.size.y != _thd.new_size.y ||
 	    _thd.outersize.x != _thd.new_outersize.x ||
 	    _thd.outersize.y != _thd.new_outersize.y) {
-		// clear the old selection?
+		/* clear the old selection? */
 		if (_thd.drawstyle) SetSelectionTilesDirty();
 
 		_thd.drawstyle = _thd.new_drawstyle;
@@ -1934,15 +2013,16 @@
 		_thd.outersize = _thd.new_outersize;
 		_thd.dirty = 0xff;
 
-		// draw the new selection?
+		/* draw the new selection? */
 		if (_thd.new_drawstyle) SetSelectionTilesDirty();
 	}
 }
 
-// highlighting tiles while only going over them with the mouse
-void VpStartPlaceSizing(TileIndex tile, int user)
+/** highlighting tiles while only going over them with the mouse */
+void VpStartPlaceSizing(TileIndex tile, byte method, byte process)
 {
-	_thd.userdata = user;
+	_thd.select_method = method;
+	_thd.select_proc   = process;
 	_thd.selend.x = TileX(tile) * TILE_SIZE;
 	_thd.selstart.x = TileX(tile) * TILE_SIZE;
 	_thd.selend.y = TileY(tile) * TILE_SIZE;
@@ -1989,7 +2069,7 @@
 	_special_mouse_mode = WSM_PRESIZE;
 }
 
-/* returns information about the 2x1 piece to be build.
+/** returns information about the 2x1 piece to be build.
  * The lower bits (0-3) are the track type. */
 static byte Check2x1AutoRail(int mode)
 {
@@ -2031,8 +2111,9 @@
  * north-south (DIR_S) to obtain the same results with less code. This is what
  * the return value signifies.
  * @param style HighLightStyle dragging style
- * @param start_tile, end_tile start and end tile of drag
- * @param boolean value which when true means start/end should be swapped */
+ * @param start_tile start tile of drag
+ * @param end_tile end tile of drag
+ * @return boolean value which when true means start/end should be swapped */
 static bool SwapDirection(HighLightStyle style, TileIndex start_tile, TileIndex end_tile)
 {
 	uint start_x = TileX(start_tile);
@@ -2057,7 +2138,7 @@
 * To correctly get the height difference we need the direction we are dragging
 * in, as well as with what kind of tool we are dragging. For example a horizontal
 * autorail tool that starts in bottom and ends at the top of a tile will need the
-* maximum of SW,S and SE,N corners respectively. This is handled by the lookup table below
+* maximum of SW, S and SE, N corners respectively. This is handled by the lookup table below
 * See _tileoffs_by_dir in map.c for the direction enums if you can't figure out
 * the values yourself.
 * @param style HightlightStyle of drag. This includes direction and style (autorail, rect, etc.)
@@ -2140,7 +2221,7 @@
 
 static const StringID measure_strings_length[] = {STR_NULL, STR_MEASURE_LENGTH, STR_MEASURE_LENGTH_HEIGHTDIFF};
 
-// while dragging
+/** while dragging */
 static void CalcRaildirsDrawstyle(TileHighlightData *thd, int x, int y, int method)
 {
 	HighLightStyle b;
@@ -2391,7 +2472,7 @@
 	_thd.selend.y = y;
 }
 
-// while dragging
+/** while dragging */
 bool VpHandlePlaceSizingDrag()
 {
 	Window *w;
@@ -2399,16 +2480,17 @@
 
 	if (_special_mouse_mode != WSM_SIZING) return true;
 
-	e.we.place.userdata = _thd.userdata;
-
-	// stop drag mode if the window has been closed
-	w = FindWindowById(_thd.window_class,_thd.window_number);
+	e.we.place.select_method = _thd.select_method;
+	e.we.place.select_proc   = _thd.select_proc;
+
+	/* stop drag mode if the window has been closed */
+	w = FindWindowById(_thd.window_class, _thd.window_number);
 	if (w == NULL) {
 		ResetObjectToPlace();
 		return false;
 	}
 
-	// while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() )
+	/* while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() ) */
 	if (_left_button_down) {
 		e.event = WE_PLACE_DRAG;
 		e.we.place.pt = GetTileBelowCursor();
@@ -2416,12 +2498,12 @@
 		return false;
 	}
 
-	// mouse button released..
-	// keep the selected tool, but reset it to the original mode.
+	/* mouse button released..
+	 * keep the selected tool, but reset it to the original mode. */
 	_special_mouse_mode = WSM_NONE;
 	if (_thd.next_drawstyle == HT_RECT) {
 		_thd.place_mode = VHM_RECT;
-	} else if ((e.we.place.userdata & 0xF) == VPM_SIGNALDIRS) { // some might call this a hack... -- Dominik
+	} else if (e.we.place.select_method == VPM_SIGNALDIRS) { // some might call this a hack... -- Dominik
 		_thd.place_mode = VHM_RECT;
 	} else if (_thd.next_drawstyle & HT_LINE) {
 		_thd.place_mode = VHM_RAIL;
@@ -2432,7 +2514,7 @@
 	}
 	SetTileSelectSize(1, 1);
 
-	// and call the mouseup event.
+	/* and call the mouseup event. */
 	e.event = WE_PLACE_MOUSEUP;
 	e.we.place.pt = _thd.selend;
 	e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y);
@@ -2453,7 +2535,7 @@
 {
 	Window *w;
 
-	// undo clicking on button
+	/* undo clicking on button */
 	if (_thd.place_mode != 0) {
 		_thd.place_mode = 0;
 		w = FindWindowById(_thd.window_class, _thd.window_number);