src/viewport.c
changeset 5584 1111b4d36e35
parent 5583 136d8764c7e6
child 5585 189f096bc9a3
equal deleted inserted replaced
5583:136d8764c7e6 5584:1111b4d36e35
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 #include "debug.h"
       
     6 #include "functions.h"
       
     7 #include "gui.h"
       
     8 #include "spritecache.h"
       
     9 #include "strings.h"
       
    10 #include "table/sprites.h"
       
    11 #include "table/strings.h"
       
    12 #include "map.h"
       
    13 #include "viewport.h"
       
    14 #include "window.h"
       
    15 #include "vehicle.h"
       
    16 #include "station.h"
       
    17 #include "gfx.h"
       
    18 #include "town.h"
       
    19 #include "signs.h"
       
    20 #include "waypoint.h"
       
    21 #include "variables.h"
       
    22 #include "train.h"
       
    23 
       
    24 #define VIEWPORT_DRAW_MEM (65536 * 2)
       
    25 
       
    26 /* viewport.c */
       
    27 // XXX - maximum viewports is maximum windows - 2 (main toolbar + status bar)
       
    28 static ViewPort _viewports[25 - 2];
       
    29 static uint32 _active_viewports;    ///< bitmasked variable where each bit signifies if a viewport is in use or not
       
    30 assert_compile(lengthof(_viewports) < sizeof(_active_viewports) * 8);
       
    31 
       
    32 static bool _added_tile_sprite;
       
    33 static bool _offset_ground_sprites;
       
    34 
       
    35 /* The in-game coordiante system looks like this *
       
    36  *                                               *
       
    37  *                    ^ Z                        *
       
    38  *                    |                          *
       
    39  *                    |                          *
       
    40  *                    |                          *
       
    41  *                    |                          *
       
    42  *                 /     \                       *
       
    43  *              /           \                    *
       
    44  *           /                 \                 *
       
    45  *        /                       \              *
       
    46  *   X <                             > Y         *
       
    47  */
       
    48 
       
    49 typedef struct StringSpriteToDraw {
       
    50 	uint16 string;
       
    51 	uint16 color;
       
    52 	struct StringSpriteToDraw *next;
       
    53 	int32 x;
       
    54 	int32 y;
       
    55 	uint32 params[2];
       
    56 	uint16 width;
       
    57 } StringSpriteToDraw;
       
    58 
       
    59 typedef struct TileSpriteToDraw {
       
    60 	uint32 image;
       
    61 	struct TileSpriteToDraw *next;
       
    62 	int32 x;
       
    63 	int32 y;
       
    64 	byte z;
       
    65 } TileSpriteToDraw;
       
    66 
       
    67 typedef struct ChildScreenSpriteToDraw {
       
    68 	uint32 image;
       
    69 	int32 x;
       
    70 	int32 y;
       
    71 	struct ChildScreenSpriteToDraw *next;
       
    72 } ChildScreenSpriteToDraw;
       
    73 
       
    74 typedef struct ParentSpriteToDraw {
       
    75 	uint32 image;
       
    76 	int32 left;
       
    77 	int32 top;
       
    78 	int32 right;
       
    79 	int32 bottom;
       
    80 	int32 xmin;
       
    81 	int32 ymin;
       
    82 	int32 xmax;
       
    83 	int32 ymax;
       
    84 	ChildScreenSpriteToDraw *child;
       
    85 	byte unk16;
       
    86 	byte zmin;
       
    87 	byte zmax;
       
    88 } ParentSpriteToDraw;
       
    89 
       
    90 // Quick hack to know how much memory to reserve when allocating from the spritelist
       
    91 // to prevent a buffer overflow.
       
    92 #define LARGEST_SPRITELIST_STRUCT ParentSpriteToDraw
       
    93 
       
    94 typedef struct ViewportDrawer {
       
    95 	DrawPixelInfo dpi;
       
    96 
       
    97 	byte *spritelist_mem;
       
    98 	const byte *eof_spritelist_mem;
       
    99 
       
   100 	StringSpriteToDraw **last_string, *first_string;
       
   101 	TileSpriteToDraw **last_tile, *first_tile;
       
   102 
       
   103 	ChildScreenSpriteToDraw **last_child;
       
   104 
       
   105 	ParentSpriteToDraw **parent_list;
       
   106 	ParentSpriteToDraw * const *eof_parent_list;
       
   107 
       
   108 	byte combine_sprites;
       
   109 
       
   110 	int offs_x, offs_y; // used when drawing ground sprites relative
       
   111 } ViewportDrawer;
       
   112 
       
   113 static ViewportDrawer *_cur_vd;
       
   114 
       
   115 TileHighlightData _thd;
       
   116 static TileInfo *_cur_ti;
       
   117 
       
   118 extern void SmallMapCenterOnCurrentPos(Window *w);
       
   119 
       
   120 static Point MapXYZToViewport(const ViewPort *vp, uint x, uint y, uint z)
       
   121 {
       
   122 	Point p = RemapCoords(x, y, z);
       
   123 	p.x -= vp->virtual_width / 2;
       
   124 	p.y -= vp->virtual_height / 2;
       
   125 	return p;
       
   126 }
       
   127 
       
   128 void InitViewports(void) {
       
   129 	memset(_viewports, 0, sizeof(_viewports));
       
   130 	_active_viewports = 0;
       
   131 }
       
   132 
       
   133 void DeleteWindowViewport(Window *w)
       
   134 {
       
   135 	CLRBIT(_active_viewports, w->viewport - _viewports);
       
   136 	w->viewport->width = 0;
       
   137 	w->viewport = NULL;
       
   138 }
       
   139 
       
   140 void AssignWindowViewport(Window *w, int x, int y,
       
   141 	int width, int height, uint32 follow_flags, byte zoom)
       
   142 {
       
   143 	ViewPort *vp;
       
   144 	Point pt;
       
   145 	uint32 bit;
       
   146 
       
   147 	for (vp = _viewports, bit = 0; ; vp++, bit++) {
       
   148 		assert(vp != endof(_viewports));
       
   149 		if (vp->width == 0) break;
       
   150 	}
       
   151 	SETBIT(_active_viewports, bit);
       
   152 
       
   153 	vp->left = x + w->left;
       
   154 	vp->top = y + w->top;
       
   155 	vp->width = width;
       
   156 	vp->height = height;
       
   157 
       
   158 	vp->zoom = zoom;
       
   159 
       
   160 	vp->virtual_width = width << zoom;
       
   161 	vp->virtual_height = height << zoom;
       
   162 
       
   163 	if (follow_flags & 0x80000000) {
       
   164 		const Vehicle *veh;
       
   165 
       
   166 		WP(w, vp_d).follow_vehicle = (VehicleID)(follow_flags & 0xFFFF);
       
   167 		veh = GetVehicle(WP(w, vp_d).follow_vehicle);
       
   168 		pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
       
   169 	} else {
       
   170 		uint x = TileX(follow_flags) * TILE_SIZE;
       
   171 		uint y = TileY(follow_flags) * TILE_SIZE;
       
   172 
       
   173 		WP(w, vp_d).follow_vehicle = INVALID_VEHICLE;
       
   174 		pt = MapXYZToViewport(vp, x, y, GetSlopeZ(x, y));
       
   175 	}
       
   176 
       
   177 	WP(w, vp_d).scrollpos_x = pt.x;
       
   178 	WP(w, vp_d).scrollpos_y = pt.y;
       
   179 	w->viewport = vp;
       
   180 	vp->virtual_left = 0;//pt.x;
       
   181 	vp->virtual_top = 0;//pt.y;
       
   182 }
       
   183 
       
   184 static Point _vp_move_offs;
       
   185 
       
   186 static void DoSetViewportPosition(Window* const *wz, int left, int top, int width, int height)
       
   187 {
       
   188 
       
   189 	for (; wz != _last_z_window; wz++) {
       
   190 		const Window *w = *wz;
       
   191 
       
   192 		if (left + width > w->left &&
       
   193 				w->left + w->width > left &&
       
   194 				top + height > w->top &&
       
   195 				w->top + w->height > top) {
       
   196 
       
   197 			if (left < w->left) {
       
   198 				DoSetViewportPosition(wz, left, top, w->left - left, height);
       
   199 				DoSetViewportPosition(wz, left + (w->left - left), top, width - (w->left - left), height);
       
   200 				return;
       
   201 			}
       
   202 
       
   203 			if (left + width > w->left + w->width) {
       
   204 				DoSetViewportPosition(wz, left, top, (w->left + w->width - left), height);
       
   205 				DoSetViewportPosition(wz, left + (w->left + w->width - left), top, width - (w->left + w->width - left) , height);
       
   206 				return;
       
   207 			}
       
   208 
       
   209 			if (top < w->top) {
       
   210 				DoSetViewportPosition(wz, left, top, width, (w->top - top));
       
   211 				DoSetViewportPosition(wz, left, top + (w->top - top), width, height - (w->top - top));
       
   212 				return;
       
   213 			}
       
   214 
       
   215 			if (top + height > w->top + w->height) {
       
   216 				DoSetViewportPosition(wz, left, top, width, (w->top + w->height - top));
       
   217 				DoSetViewportPosition(wz, left, top + (w->top + w->height - top), width , height - (w->top + w->height - top));
       
   218 				return;
       
   219 			}
       
   220 
       
   221 			return;
       
   222 		}
       
   223 	}
       
   224 
       
   225 	{
       
   226 		int xo = _vp_move_offs.x;
       
   227 		int yo = _vp_move_offs.y;
       
   228 
       
   229 		if (abs(xo) >= width || abs(yo) >= height) {
       
   230 			/* fully_outside */
       
   231 			RedrawScreenRect(left, top, left + width, top + height);
       
   232 			return;
       
   233 		}
       
   234 
       
   235 		GfxScroll(left, top, width, height, xo, yo);
       
   236 
       
   237 		if (xo > 0) {
       
   238 			RedrawScreenRect(left, top, xo + left, top + height);
       
   239 			left += xo;
       
   240 			width -= xo;
       
   241 		} else if (xo < 0) {
       
   242 			RedrawScreenRect(left+width+xo, top, left+width, top + height);
       
   243 			width += xo;
       
   244 		}
       
   245 
       
   246 		if (yo > 0) {
       
   247 			RedrawScreenRect(left, top, width+left, top + yo);
       
   248 		} else if (yo < 0) {
       
   249 			RedrawScreenRect(left, top + height + yo, width+left, top + height);
       
   250 		}
       
   251 	}
       
   252 }
       
   253 
       
   254 static void SetViewportPosition(Window *w, int x, int y)
       
   255 {
       
   256 	ViewPort *vp = w->viewport;
       
   257 	int old_left = vp->virtual_left;
       
   258 	int old_top = vp->virtual_top;
       
   259 	int i;
       
   260 	int left, top, width, height;
       
   261 
       
   262 	vp->virtual_left = x;
       
   263 	vp->virtual_top = y;
       
   264 
       
   265 	old_left >>= vp->zoom;
       
   266 	old_top >>= vp->zoom;
       
   267 	x >>= vp->zoom;
       
   268 	y >>= vp->zoom;
       
   269 
       
   270 	old_left -= x;
       
   271 	old_top -= y;
       
   272 
       
   273 	if (old_top == 0 && old_left == 0) return;
       
   274 
       
   275 	_vp_move_offs.x = old_left;
       
   276 	_vp_move_offs.y = old_top;
       
   277 
       
   278 	left = vp->left;
       
   279 	top = vp->top;
       
   280 	width = vp->width;
       
   281 	height = vp->height;
       
   282 
       
   283 	if (left < 0) {
       
   284 		width += left;
       
   285 		left = 0;
       
   286 	}
       
   287 
       
   288 	i = left + width - _screen.width;
       
   289 	if (i >= 0) width -= i;
       
   290 
       
   291 	if (width > 0) {
       
   292 		if (top < 0) {
       
   293 			height += top;
       
   294 			top = 0;
       
   295 		}
       
   296 
       
   297 		i = top + height - _screen.height;
       
   298 		if (i >= 0) height -= i;
       
   299 
       
   300 		if (height > 0) DoSetViewportPosition(FindWindowZPosition(w) + 1, left, top, width, height);
       
   301 	}
       
   302 }
       
   303 
       
   304 
       
   305 ViewPort *IsPtInWindowViewport(const Window *w, int x, int y)
       
   306 {
       
   307 	ViewPort *vp = w->viewport;
       
   308 
       
   309 	if (vp != NULL &&
       
   310 	    IS_INT_INSIDE(x, vp->left, vp->left + vp->width) &&
       
   311 			IS_INT_INSIDE(y, vp->top, vp->top + vp->height))
       
   312 		return vp;
       
   313 
       
   314 	return NULL;
       
   315 }
       
   316 
       
   317 static Point TranslateXYToTileCoord(const ViewPort *vp, int x, int y)
       
   318 {
       
   319 	Point pt;
       
   320 	int a,b;
       
   321 	uint z;
       
   322 
       
   323 	if ( (uint)(x -= vp->left) >= (uint)vp->width ||
       
   324 				(uint)(y -= vp->top) >= (uint)vp->height) {
       
   325 				Point pt = {-1, -1};
       
   326 				return pt;
       
   327 	}
       
   328 
       
   329 	x = ((x << vp->zoom) + vp->virtual_left) >> 2;
       
   330 	y = ((y << vp->zoom) + vp->virtual_top) >> 1;
       
   331 
       
   332 	a = y-x;
       
   333 	b = y+x;
       
   334 
       
   335 	/* we need to move variables in to the valid range, as the
       
   336 	 * GetTileZoomCenterWindow() function can call here with invalid x and/or y,
       
   337 	 * when the user tries to zoom out along the sides of the map */
       
   338 	a = clamp(a, 0, (int)(MapMaxX() * TILE_SIZE) - 1);
       
   339 	b = clamp(b, 0, (int)(MapMaxY() * TILE_SIZE) - 1);
       
   340 
       
   341 	z = GetSlopeZ(a,     b    ) / 2;
       
   342 	z = GetSlopeZ(a + z, b + z) / 2;
       
   343 	z = GetSlopeZ(a + z, b + z) / 2;
       
   344 	z = GetSlopeZ(a + z, b + z) / 2;
       
   345 	z = GetSlopeZ(a + z, b + z) / 2;
       
   346 
       
   347 	pt.x = a + z;
       
   348 	pt.y = b + z;
       
   349 
       
   350 	return pt;
       
   351 }
       
   352 
       
   353 /* When used for zooming, check area below current coordinates (x,y)
       
   354  * and return the tile of the zoomed out/in position (zoom_x, zoom_y)
       
   355  * when you just want the tile, make x = zoom_x and y = zoom_y */
       
   356 static Point GetTileFromScreenXY(int x, int y, int zoom_x, int zoom_y)
       
   357 {
       
   358 	Window *w;
       
   359 	ViewPort *vp;
       
   360 	Point pt;
       
   361 
       
   362 	if ( (w = FindWindowFromPt(x, y)) != NULL &&
       
   363 			 (vp = IsPtInWindowViewport(w, x, y)) != NULL)
       
   364 				return TranslateXYToTileCoord(vp, zoom_x, zoom_y);
       
   365 
       
   366 	pt.y = pt.x = -1;
       
   367 	return pt;
       
   368 }
       
   369 
       
   370 Point GetTileBelowCursor(void)
       
   371 {
       
   372 	return GetTileFromScreenXY(_cursor.pos.x, _cursor.pos.y, _cursor.pos.x, _cursor.pos.y);
       
   373 }
       
   374 
       
   375 
       
   376 Point GetTileZoomCenterWindow(bool in, Window * w)
       
   377 {
       
   378 	int x, y;
       
   379 	ViewPort * vp;
       
   380 
       
   381 	vp = w->viewport;
       
   382 
       
   383 	if (in) {
       
   384 		x = ((_cursor.pos.x - vp->left) >> 1) + (vp->width >> 2);
       
   385 		y = ((_cursor.pos.y - vp->top) >> 1) + (vp->height >> 2);
       
   386 	} else {
       
   387 		x = vp->width - (_cursor.pos.x - vp->left);
       
   388 		y = vp->height - (_cursor.pos.y - vp->top);
       
   389 	}
       
   390 	/* Get the tile below the cursor and center on the zoomed-out center */
       
   391 	return GetTileFromScreenXY(_cursor.pos.x, _cursor.pos.y, x + vp->left, y + vp->top);
       
   392 }
       
   393 
       
   394 /** Update the status of the zoom-buttons according to the zoom-level
       
   395  * of the viewport. This will update their status and invalidate accordingly
       
   396  * @param window pointer to the window that has the zoom buttons
       
   397  * @param vp pointer to the viewport whose zoom-level the buttons represent
       
   398  * @param widget_zoom_in widget index for window with zoom-in button
       
   399  * @param widget_zoom_out widget index for window with zoom-out button */
       
   400 void HandleZoomMessage(Window *w, const ViewPort *vp, byte widget_zoom_in, byte widget_zoom_out)
       
   401 {
       
   402 	SetWindowWidgetDisabledState(w, widget_zoom_in, vp->zoom == 0);
       
   403 	InvalidateWidget(w, widget_zoom_in);
       
   404 
       
   405 	SetWindowWidgetDisabledState(w, widget_zoom_out, vp->zoom == 2);
       
   406 	InvalidateWidget(w, widget_zoom_out);
       
   407 }
       
   408 
       
   409 void DrawGroundSpriteAt(uint32 image, int32 x, int32 y, byte z)
       
   410 {
       
   411 	ViewportDrawer *vd = _cur_vd;
       
   412 	TileSpriteToDraw *ts;
       
   413 
       
   414 	assert((image & SPRITE_MASK) < MAX_SPRITES);
       
   415 
       
   416 	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
       
   417 		DEBUG(sprite, 0, "Out of sprite memory");
       
   418 		return;
       
   419 	}
       
   420 	ts = (TileSpriteToDraw*)vd->spritelist_mem;
       
   421 
       
   422 	vd->spritelist_mem += sizeof(TileSpriteToDraw);
       
   423 
       
   424 	ts->image = image;
       
   425 	ts->next = NULL;
       
   426 	ts->x = x;
       
   427 	ts->y = y;
       
   428 	ts->z = z;
       
   429 	*vd->last_tile = ts;
       
   430 	vd->last_tile = &ts->next;
       
   431 }
       
   432 
       
   433 void DrawGroundSprite(uint32 image)
       
   434 {
       
   435 	if (_offset_ground_sprites) {
       
   436 		// offset ground sprite because of foundation?
       
   437 		AddChildSpriteScreen(image, _cur_vd->offs_x, _cur_vd->offs_y);
       
   438 	} else {
       
   439 		_added_tile_sprite = true;
       
   440 		DrawGroundSpriteAt(image, _cur_ti->x, _cur_ti->y, _cur_ti->z);
       
   441 	}
       
   442 }
       
   443 
       
   444 
       
   445 void OffsetGroundSprite(int x, int y)
       
   446 {
       
   447 	_cur_vd->offs_x = x;
       
   448 	_cur_vd->offs_y = y;
       
   449 	_offset_ground_sprites = true;
       
   450 }
       
   451 
       
   452 static void AddCombinedSprite(uint32 image, int x, int y, byte z)
       
   453 {
       
   454 	const ViewportDrawer *vd = _cur_vd;
       
   455 	Point pt = RemapCoords(x, y, z);
       
   456 	const Sprite* spr = GetSprite(image & SPRITE_MASK);
       
   457 
       
   458 	if (pt.x + spr->x_offs >= vd->dpi.left + vd->dpi.width ||
       
   459 			pt.x + spr->x_offs + spr->width <= vd->dpi.left ||
       
   460 			pt.y + spr->y_offs >= vd->dpi.top + vd->dpi.height ||
       
   461 			pt.y + spr->y_offs + spr->height <= vd->dpi.top)
       
   462 		return;
       
   463 
       
   464 	AddChildSpriteScreen(image, pt.x - vd->parent_list[-1]->left, pt.y - vd->parent_list[-1]->top);
       
   465 }
       
   466 
       
   467 
       
   468 void AddSortableSpriteToDraw(uint32 image, int x, int y, int w, int h, byte dz, byte z)
       
   469 {
       
   470 	ViewportDrawer *vd = _cur_vd;
       
   471 	ParentSpriteToDraw *ps;
       
   472 	const Sprite *spr;
       
   473 	Point pt;
       
   474 
       
   475 	assert((image & SPRITE_MASK) < MAX_SPRITES);
       
   476 
       
   477 	if (vd->combine_sprites == 2) {
       
   478 		AddCombinedSprite(image, x, y, z);
       
   479 		return;
       
   480 	}
       
   481 
       
   482 	vd->last_child = NULL;
       
   483 
       
   484 	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
       
   485 		DEBUG(sprite, 0, "Out of sprite memory");
       
   486 		return;
       
   487 	}
       
   488 	ps = (ParentSpriteToDraw*)vd->spritelist_mem;
       
   489 
       
   490 	if (vd->parent_list >= vd->eof_parent_list) {
       
   491 		// This can happen rarely, mostly when you zoom out completely
       
   492 		//  and have a lot of stuff that moves (and is added to the
       
   493 		//  sort-list, this function). To solve it, increase
       
   494 		//  parent_list somewhere below to a higher number.
       
   495 		// This can not really hurt you, it just gives some black
       
   496 		//  spots on the screen ;)
       
   497 		DEBUG(sprite, 0, "Out of sprite memory (parent_list)");
       
   498 		return;
       
   499 	}
       
   500 
       
   501 	pt = RemapCoords(x, y, z);
       
   502 	spr = GetSprite(image & SPRITE_MASK);
       
   503 	if ((ps->left   = (pt.x += spr->x_offs)) >= vd->dpi.left + vd->dpi.width ||
       
   504 			(ps->right  = (pt.x +  spr->width )) <= vd->dpi.left ||
       
   505 			(ps->top    = (pt.y += spr->y_offs)) >= vd->dpi.top + vd->dpi.height ||
       
   506 			(ps->bottom = (pt.y +  spr->height)) <= vd->dpi.top) {
       
   507 		return;
       
   508 	}
       
   509 
       
   510 	vd->spritelist_mem += sizeof(ParentSpriteToDraw);
       
   511 
       
   512 	ps->image = image;
       
   513 	ps->xmin = x;
       
   514 	ps->xmax = x + w - 1;
       
   515 
       
   516 	ps->ymin = y;
       
   517 	ps->ymax = y + h - 1;
       
   518 
       
   519 	ps->zmin = z;
       
   520 	ps->zmax = z + dz - 1;
       
   521 
       
   522 	ps->unk16 = 0;
       
   523 	ps->child = NULL;
       
   524 	vd->last_child = &ps->child;
       
   525 
       
   526 	*vd->parent_list++ = ps;
       
   527 
       
   528 	if (vd->combine_sprites == 1) vd->combine_sprites = 2;
       
   529 }
       
   530 
       
   531 void StartSpriteCombine(void)
       
   532 {
       
   533 	_cur_vd->combine_sprites = 1;
       
   534 }
       
   535 
       
   536 void EndSpriteCombine(void)
       
   537 {
       
   538 	_cur_vd->combine_sprites = 0;
       
   539 }
       
   540 
       
   541 void AddChildSpriteScreen(uint32 image, int x, int y)
       
   542 {
       
   543 	ViewportDrawer *vd = _cur_vd;
       
   544 	ChildScreenSpriteToDraw *cs;
       
   545 
       
   546 	assert((image & SPRITE_MASK) < MAX_SPRITES);
       
   547 
       
   548 	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
       
   549 		DEBUG(sprite, 0, "Out of sprite memory");
       
   550 		return;
       
   551 	}
       
   552 	cs = (ChildScreenSpriteToDraw*)vd->spritelist_mem;
       
   553 
       
   554 	if (vd->last_child == NULL) return;
       
   555 
       
   556 	vd->spritelist_mem += sizeof(ChildScreenSpriteToDraw);
       
   557 
       
   558 	*vd->last_child = cs;
       
   559 	vd->last_child = &cs->next;
       
   560 
       
   561 	cs->image = image;
       
   562 	cs->x = x;
       
   563 	cs->y = y;
       
   564 	cs->next = NULL;
       
   565 }
       
   566 
       
   567 /* Returns a StringSpriteToDraw */
       
   568 void *AddStringToDraw(int x, int y, StringID string, uint32 params_1, uint32 params_2)
       
   569 {
       
   570 	ViewportDrawer *vd = _cur_vd;
       
   571 	StringSpriteToDraw *ss;
       
   572 
       
   573 	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
       
   574 		DEBUG(sprite, 0, "Out of sprite memory");
       
   575 		return NULL;
       
   576 	}
       
   577 	ss = (StringSpriteToDraw*)vd->spritelist_mem;
       
   578 
       
   579 	vd->spritelist_mem += sizeof(StringSpriteToDraw);
       
   580 
       
   581 	ss->string = string;
       
   582 	ss->next = NULL;
       
   583 	ss->x = x;
       
   584 	ss->y = y;
       
   585 	ss->params[0] = params_1;
       
   586 	ss->params[1] = params_2;
       
   587 	ss->width = 0;
       
   588 
       
   589 	*vd->last_string = ss;
       
   590 	vd->last_string = &ss->next;
       
   591 
       
   592 	return ss;
       
   593 }
       
   594 
       
   595 
       
   596 static void DrawSelectionSprite(uint32 image, const TileInfo *ti)
       
   597 {
       
   598 	if (_added_tile_sprite && !(_thd.drawstyle & HT_LINE)) { // draw on real ground
       
   599 		DrawGroundSpriteAt(image, ti->x, ti->y, ti->z + 7);
       
   600 	} else { // draw on top of foundation
       
   601 		AddSortableSpriteToDraw(image, ti->x, ti->y, 0x10, 0x10, 1, ti->z + 7);
       
   602 	}
       
   603 }
       
   604 
       
   605 static bool IsPartOfAutoLine(int px, int py)
       
   606 {
       
   607 	px -= _thd.selstart.x;
       
   608 	py -= _thd.selstart.y;
       
   609 
       
   610 	switch (_thd.drawstyle) {
       
   611 	case HT_LINE | HT_DIR_X:  return py == 0; // x direction
       
   612 	case HT_LINE | HT_DIR_Y:  return px == 0; // y direction
       
   613 	case HT_LINE | HT_DIR_HU: return px == -py || px == -py - 16; // horizontal upper
       
   614 	case HT_LINE | HT_DIR_HL: return px == -py || px == -py + 16; // horizontal lower
       
   615 	case HT_LINE | HT_DIR_VL: return px == py || px == py + 16; // vertival left
       
   616 	case HT_LINE | HT_DIR_VR: return px == py || px == py - 16; // vertical right
       
   617 	default:
       
   618 		NOT_REACHED();
       
   619 	}
       
   620 
       
   621 	/* useless, but avoids compiler warning this way */
       
   622 	return 0;
       
   623 }
       
   624 
       
   625 // [direction][side]
       
   626 static const int _AutorailType[6][2] = {
       
   627 	{ HT_DIR_X,  HT_DIR_X },
       
   628 	{ HT_DIR_Y,  HT_DIR_Y },
       
   629 	{ HT_DIR_HU, HT_DIR_HL },
       
   630 	{ HT_DIR_HL, HT_DIR_HU },
       
   631 	{ HT_DIR_VL, HT_DIR_VR },
       
   632 	{ HT_DIR_VR, HT_DIR_VL }
       
   633 };
       
   634 
       
   635 #include "table/autorail.h"
       
   636 
       
   637 static void DrawTileSelection(const TileInfo *ti)
       
   638 {
       
   639 	uint32 image;
       
   640 
       
   641 	// Draw a red error square?
       
   642 	if (_thd.redsq != 0 && _thd.redsq == ti->tile) {
       
   643 		DrawSelectionSprite(PALETTE_TILE_RED_PULSATING | (SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh]), ti);
       
   644 		return;
       
   645 	}
       
   646 
       
   647 	// no selection active?
       
   648 	if (_thd.drawstyle == 0) return;
       
   649 
       
   650 	// Inside the inner area?
       
   651 	if (IS_INSIDE_1D(ti->x, _thd.pos.x, _thd.size.x) &&
       
   652 			IS_INSIDE_1D(ti->y, _thd.pos.y, _thd.size.y)) {
       
   653 		if (_thd.drawstyle & HT_RECT) {
       
   654 			image = SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh];
       
   655 			if (_thd.make_square_red) image |= PALETTE_SEL_TILE_RED;
       
   656 			DrawSelectionSprite(image, ti);
       
   657 		} else if (_thd.drawstyle & HT_POINT) {
       
   658 			// Figure out the Z coordinate for the single dot.
       
   659 			byte z = ti->z;
       
   660 			if (ti->tileh & SLOPE_N) {
       
   661 				z += TILE_HEIGHT;
       
   662 				if (ti->tileh == SLOPE_STEEP_N) z += TILE_HEIGHT;
       
   663 			}
       
   664 			DrawGroundSpriteAt(_cur_dpi->zoom != 2 ? SPR_DOT : SPR_DOT_SMALL, ti->x, ti->y, z);
       
   665 		} else if (_thd.drawstyle & HT_RAIL /*&& _thd.place_mode == VHM_RAIL*/) {
       
   666 			// autorail highlight piece under cursor
       
   667 			uint type = _thd.drawstyle & 0xF;
       
   668 			assert(type <= 5);
       
   669 			image = SPR_AUTORAIL_BASE + _AutorailTilehSprite[ti->tileh][_AutorailType[type][0]];
       
   670 
       
   671 			if (_thd.make_square_red) image |= PALETTE_SEL_TILE_RED;
       
   672 			DrawSelectionSprite(image, ti);
       
   673 
       
   674 		} else if (IsPartOfAutoLine(ti->x, ti->y)) {
       
   675 			// autorail highlighting long line
       
   676 			int dir = _thd.drawstyle & ~0xF0;
       
   677 			uint side;
       
   678 
       
   679 			if (dir < 2) {
       
   680 				side = 0;
       
   681 			} else {
       
   682 				TileIndex start = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
       
   683 				int diffx = myabs(TileX(start) - TileX(ti->tile));
       
   684 				int diffy = myabs(TileY(start) - TileY(ti->tile));
       
   685 				side = myabs(diffx - diffy);
       
   686 			}
       
   687 
       
   688 			image = SPR_AUTORAIL_BASE + _AutorailTilehSprite[ti->tileh][_AutorailType[dir][side]];
       
   689 
       
   690 			if (_thd.make_square_red) image |= PALETTE_SEL_TILE_RED;
       
   691 			DrawSelectionSprite(image, ti);
       
   692 		}
       
   693 		return;
       
   694 	}
       
   695 
       
   696 	// Check if it's inside the outer area?
       
   697 	if (_thd.outersize.x &&
       
   698 			_thd.size.x < _thd.size.x + _thd.outersize.x &&
       
   699 			IS_INSIDE_1D(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
       
   700 			IS_INSIDE_1D(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
       
   701 		// Draw a blue rect.
       
   702 		DrawSelectionSprite(PALETTE_SEL_TILE_BLUE | (SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh]), ti);
       
   703 		return;
       
   704 	}
       
   705 }
       
   706 
       
   707 static void ViewportAddLandscape(void)
       
   708 {
       
   709 	ViewportDrawer *vd = _cur_vd;
       
   710 	int x, y, width, height;
       
   711 	TileInfo ti;
       
   712 	bool direction;
       
   713 
       
   714 	_cur_ti = &ti;
       
   715 
       
   716 	// Transform into tile coordinates and round to closest full tile
       
   717 	x = ((vd->dpi.top >> 1) - (vd->dpi.left >> 2)) & ~0xF;
       
   718 	y = ((vd->dpi.top >> 1) + (vd->dpi.left >> 2) - 0x10) & ~0xF;
       
   719 
       
   720 	// determine size of area
       
   721 	{
       
   722 		Point pt = RemapCoords(x, y, 241);
       
   723 		width = (vd->dpi.left + vd->dpi.width - pt.x + 95) >> 6;
       
   724 		height = (vd->dpi.top + vd->dpi.height - pt.y) >> 5 << 1;
       
   725 	}
       
   726 
       
   727 	assert(width > 0);
       
   728 	assert(height > 0);
       
   729 
       
   730 	direction = false;
       
   731 
       
   732 	do {
       
   733 		int width_cur = width;
       
   734 		int x_cur = x;
       
   735 		int y_cur = y;
       
   736 
       
   737 		do {
       
   738 			TileType tt;
       
   739 
       
   740 			ti.x = x_cur;
       
   741 			ti.y = y_cur;
       
   742 			if (0 <= x_cur && x_cur < (int)MapMaxX() * TILE_SIZE &&
       
   743 					0 <= y_cur && y_cur < (int)MapMaxY() * TILE_SIZE) {
       
   744 				TileIndex tile = TileVirtXY(x_cur, y_cur);
       
   745 
       
   746 				ti.tile = tile;
       
   747 				ti.tileh = GetTileSlope(tile, &ti.z);
       
   748 				tt = GetTileType(tile);
       
   749 			} else {
       
   750 				ti.tileh = SLOPE_FLAT;
       
   751 				ti.tile = 0;
       
   752 				ti.z = 0;
       
   753 				tt = MP_VOID;
       
   754 			}
       
   755 
       
   756 			y_cur += 0x10;
       
   757 			x_cur -= 0x10;
       
   758 
       
   759 			_added_tile_sprite = false;
       
   760 			_offset_ground_sprites = false;
       
   761 
       
   762 			_tile_type_procs[tt]->draw_tile_proc(&ti);
       
   763 			DrawTileSelection(&ti);
       
   764 		} while (--width_cur);
       
   765 
       
   766 		if ((direction ^= 1) != 0) {
       
   767 			y += 0x10;
       
   768 		} else {
       
   769 			x += 0x10;
       
   770 		}
       
   771 	} while (--height);
       
   772 }
       
   773 
       
   774 
       
   775 static void ViewportAddTownNames(DrawPixelInfo *dpi)
       
   776 {
       
   777 	Town *t;
       
   778 	int left, top, right, bottom;
       
   779 
       
   780 	if (!(_display_opt & DO_SHOW_TOWN_NAMES) || _game_mode == GM_MENU)
       
   781 		return;
       
   782 
       
   783 	left = dpi->left;
       
   784 	top = dpi->top;
       
   785 	right = left + dpi->width;
       
   786 	bottom = top + dpi->height;
       
   787 
       
   788 	switch (dpi->zoom) {
       
   789 		case 0:
       
   790 			FOR_ALL_TOWNS(t) {
       
   791 				if (bottom > t->sign.top &&
       
   792 						top    < t->sign.top + 12 &&
       
   793 						right  > t->sign.left &&
       
   794 						left   < t->sign.left + t->sign.width_1) {
       
   795 					AddStringToDraw(t->sign.left + 1, t->sign.top + 1,
       
   796 						_patches.population_in_label ? STR_TOWN_LABEL_POP : STR_TOWN_LABEL,
       
   797 						t->index, t->population);
       
   798 				}
       
   799 			}
       
   800 			break;
       
   801 
       
   802 		case 1:
       
   803 			right += 2;
       
   804 			bottom += 2;
       
   805 
       
   806 			FOR_ALL_TOWNS(t) {
       
   807 				if (bottom > t->sign.top &&
       
   808 						top    < t->sign.top + 24 &&
       
   809 						right  > t->sign.left &&
       
   810 						left   < t->sign.left + t->sign.width_1*2) {
       
   811 					AddStringToDraw(t->sign.left + 1, t->sign.top + 1,
       
   812 						_patches.population_in_label ? STR_TOWN_LABEL_POP : STR_TOWN_LABEL,
       
   813 						t->index, t->population);
       
   814 				}
       
   815 			}
       
   816 			break;
       
   817 
       
   818 		default: NOT_REACHED();
       
   819 		case 2:
       
   820 			right += 4;
       
   821 			bottom += 5;
       
   822 
       
   823 			FOR_ALL_TOWNS(t) {
       
   824 				if (bottom > t->sign.top &&
       
   825 						top    < t->sign.top + 24 &&
       
   826 						right  > t->sign.left &&
       
   827 						left   < t->sign.left + t->sign.width_2*4) {
       
   828 					AddStringToDraw(t->sign.left + 5, t->sign.top + 1, STR_TOWN_LABEL_TINY_BLACK, t->index, 0);
       
   829 					AddStringToDraw(t->sign.left + 1, t->sign.top - 3, STR_TOWN_LABEL_TINY_WHITE, t->index, 0);
       
   830 				}
       
   831 			}
       
   832 			break;
       
   833 	}
       
   834 }
       
   835 
       
   836 
       
   837 static void AddStation(const Station *st, StringID str, uint16 width)
       
   838 {
       
   839 	StringSpriteToDraw *sstd;
       
   840 
       
   841 	sstd = AddStringToDraw(st->sign.left + 1, st->sign.top + 1, str, st->index, st->facilities);
       
   842 	if (sstd != NULL) {
       
   843 		sstd->color = (st->owner == OWNER_NONE || st->facilities == 0) ? 0xE : _player_colors[st->owner];
       
   844 		sstd->width = width;
       
   845 	}
       
   846 }
       
   847 
       
   848 
       
   849 static void ViewportAddStationNames(DrawPixelInfo *dpi)
       
   850 {
       
   851 	int left, top, right, bottom;
       
   852 	const Station *st;
       
   853 
       
   854 	if (!(_display_opt & DO_SHOW_STATION_NAMES) || _game_mode == GM_MENU)
       
   855 		return;
       
   856 
       
   857 	left = dpi->left;
       
   858 	top = dpi->top;
       
   859 	right = left + dpi->width;
       
   860 	bottom = top + dpi->height;
       
   861 
       
   862 	switch (dpi->zoom) {
       
   863 		case 0:
       
   864 			FOR_ALL_STATIONS(st) {
       
   865 				if (bottom > st->sign.top &&
       
   866 						top    < st->sign.top + 12 &&
       
   867 						right  > st->sign.left &&
       
   868 						left   < st->sign.left + st->sign.width_1) {
       
   869 					AddStation(st, STR_305C_0, st->sign.width_1);
       
   870 				}
       
   871 			}
       
   872 			break;
       
   873 
       
   874 		case 1:
       
   875 			right += 2;
       
   876 			bottom += 2;
       
   877 			FOR_ALL_STATIONS(st) {
       
   878 				if (bottom > st->sign.top &&
       
   879 						top    < st->sign.top + 24 &&
       
   880 						right  > st->sign.left &&
       
   881 						left   < st->sign.left + st->sign.width_1*2) {
       
   882 					AddStation(st, STR_305C_0, st->sign.width_1);
       
   883 				}
       
   884 			}
       
   885 			break;
       
   886 
       
   887 		default: NOT_REACHED();
       
   888 		case 2:
       
   889 			right += 4;
       
   890 			bottom += 5;
       
   891 			FOR_ALL_STATIONS(st) {
       
   892 				if (bottom > st->sign.top &&
       
   893 						top    < st->sign.top + 24 &&
       
   894 						right  > st->sign.left &&
       
   895 						left   < st->sign.left + st->sign.width_2*4) {
       
   896 					AddStation(st, STR_STATION_SIGN_TINY, st->sign.width_2 | 0x8000);
       
   897 				}
       
   898 			}
       
   899 			break;
       
   900 	}
       
   901 }
       
   902 
       
   903 
       
   904 static void AddSign(const Sign *si, StringID str, uint16 width)
       
   905 {
       
   906 	StringSpriteToDraw *sstd;
       
   907 
       
   908 	sstd = AddStringToDraw(si->sign.left + 1, si->sign.top + 1, str, si->str, 0);
       
   909 	if (sstd != NULL) {
       
   910 		sstd->color = (si->owner == OWNER_NONE) ? 14 : _player_colors[si->owner];
       
   911 		sstd->width = width;
       
   912 	}
       
   913 }
       
   914 
       
   915 
       
   916 static void ViewportAddSigns(DrawPixelInfo *dpi)
       
   917 {
       
   918 	const Sign *si;
       
   919 	int left, top, right, bottom;
       
   920 
       
   921 	if (!(_display_opt & DO_SHOW_SIGNS))
       
   922 		return;
       
   923 
       
   924 	left = dpi->left;
       
   925 	top = dpi->top;
       
   926 	right = left + dpi->width;
       
   927 	bottom = top + dpi->height;
       
   928 
       
   929 	switch (dpi->zoom) {
       
   930 		case 0:
       
   931 			FOR_ALL_SIGNS(si) {
       
   932 				if (bottom > si->sign.top &&
       
   933 						top    < si->sign.top + 12 &&
       
   934 						right  > si->sign.left &&
       
   935 						left   < si->sign.left + si->sign.width_1) {
       
   936 					AddSign(si, STR_2806, si->sign.width_1);
       
   937 				}
       
   938 			}
       
   939 			break;
       
   940 
       
   941 		case 1:
       
   942 			right += 2;
       
   943 			bottom += 2;
       
   944 			FOR_ALL_SIGNS(si) {
       
   945 				if (bottom > si->sign.top &&
       
   946 						top    < si->sign.top + 24 &&
       
   947 						right  > si->sign.left &&
       
   948 						left   < si->sign.left + si->sign.width_1 * 2) {
       
   949 					AddSign(si, STR_2806, si->sign.width_1);
       
   950 				}
       
   951 			}
       
   952 			break;
       
   953 
       
   954 		default: NOT_REACHED();
       
   955 		case 2:
       
   956 			right += 4;
       
   957 			bottom += 5;
       
   958 			FOR_ALL_SIGNS(si) {
       
   959 				if (bottom > si->sign.top &&
       
   960 						top    < si->sign.top + 24 &&
       
   961 						right  > si->sign.left &&
       
   962 						left   < si->sign.left + si->sign.width_2 * 4) {
       
   963 					AddSign(si, STR_2002, si->sign.width_2 | 0x8000);
       
   964 				}
       
   965 			}
       
   966 			break;
       
   967 	}
       
   968 }
       
   969 
       
   970 
       
   971 static void AddWaypoint(const Waypoint *wp, StringID str, uint16 width)
       
   972 {
       
   973 	StringSpriteToDraw *sstd;
       
   974 
       
   975 	sstd = AddStringToDraw(wp->sign.left + 1, wp->sign.top + 1, str, wp->index, 0);
       
   976 	if (sstd != NULL) {
       
   977 		sstd->color = (wp->deleted ? 0xE : 11);
       
   978 		sstd->width = width;
       
   979 	}
       
   980 }
       
   981 
       
   982 
       
   983 static void ViewportAddWaypoints(DrawPixelInfo *dpi)
       
   984 {
       
   985 	const Waypoint *wp;
       
   986 	int left, top, right, bottom;
       
   987 
       
   988 	if (!(_display_opt & DO_WAYPOINTS))
       
   989 		return;
       
   990 
       
   991 	left = dpi->left;
       
   992 	top = dpi->top;
       
   993 	right = left + dpi->width;
       
   994 	bottom = top + dpi->height;
       
   995 
       
   996 	switch (dpi->zoom) {
       
   997 		case 0:
       
   998 			FOR_ALL_WAYPOINTS(wp) {
       
   999 				if (bottom > wp->sign.top &&
       
  1000 						top    < wp->sign.top + 12 &&
       
  1001 						right  > wp->sign.left &&
       
  1002 						left   < wp->sign.left + wp->sign.width_1) {
       
  1003 					AddWaypoint(wp, STR_WAYPOINT_VIEWPORT, wp->sign.width_1);
       
  1004 				}
       
  1005 			}
       
  1006 			break;
       
  1007 
       
  1008 		case 1:
       
  1009 			right += 2;
       
  1010 			bottom += 2;
       
  1011 			FOR_ALL_WAYPOINTS(wp) {
       
  1012 				if (bottom > wp->sign.top &&
       
  1013 						top    < wp->sign.top + 24 &&
       
  1014 						right  > wp->sign.left &&
       
  1015 						left   < wp->sign.left + wp->sign.width_1*2) {
       
  1016 					AddWaypoint(wp, STR_WAYPOINT_VIEWPORT, wp->sign.width_1);
       
  1017 				}
       
  1018 			}
       
  1019 			break;
       
  1020 
       
  1021 		default: NOT_REACHED();
       
  1022 		case 2:
       
  1023 			right += 4;
       
  1024 			bottom += 5;
       
  1025 			FOR_ALL_WAYPOINTS(wp) {
       
  1026 				if (bottom > wp->sign.top &&
       
  1027 						top    < wp->sign.top + 24 &&
       
  1028 						right  > wp->sign.left &&
       
  1029 						left   < wp->sign.left + wp->sign.width_2*4) {
       
  1030 					AddWaypoint(wp, STR_WAYPOINT_VIEWPORT_TINY, wp->sign.width_2 | 0x8000);
       
  1031 				}
       
  1032 			}
       
  1033 			break;
       
  1034 	}
       
  1035 }
       
  1036 
       
  1037 void UpdateViewportSignPos(ViewportSign *sign, int left, int top, StringID str)
       
  1038 {
       
  1039 	char buffer[128];
       
  1040 	uint w;
       
  1041 
       
  1042 	sign->top = top;
       
  1043 
       
  1044 	GetString(buffer, str, lastof(buffer));
       
  1045 	w = GetStringBoundingBox(buffer).width + 3;
       
  1046 	sign->width_1 = w;
       
  1047 	sign->left = left - w / 2;
       
  1048 
       
  1049 	/* zoomed out version */
       
  1050 	_cur_fontsize = FS_SMALL;
       
  1051 	w = GetStringBoundingBox(buffer).width + 3;
       
  1052 	_cur_fontsize = FS_NORMAL;
       
  1053 	sign->width_2 = w;
       
  1054 }
       
  1055 
       
  1056 
       
  1057 static void ViewportDrawTileSprites(TileSpriteToDraw *ts)
       
  1058 {
       
  1059 	do {
       
  1060 		Point pt = RemapCoords(ts->x, ts->y, ts->z);
       
  1061 		DrawSprite(ts->image, pt.x, pt.y);
       
  1062 		ts = ts->next;
       
  1063 	} while (ts != NULL);
       
  1064 }
       
  1065 
       
  1066 static void ViewportSortParentSprites(ParentSpriteToDraw *psd[])
       
  1067 {
       
  1068 	while (*psd != NULL) {
       
  1069 		ParentSpriteToDraw* ps = *psd;
       
  1070 
       
  1071 		if (!(ps->unk16 & 1)) {
       
  1072 			ParentSpriteToDraw** psd2 = psd;
       
  1073 
       
  1074 			ps->unk16 |= 1;
       
  1075 
       
  1076 			while (*++psd2 != NULL) {
       
  1077 				ParentSpriteToDraw* ps2 = *psd2;
       
  1078 				ParentSpriteToDraw** psd3;
       
  1079 
       
  1080 				if (ps2->unk16 & 1) continue;
       
  1081 
       
  1082 				/* Decide which comparator to use, based on whether the bounding
       
  1083 				 * boxes overlap
       
  1084 				 */
       
  1085 				if (ps->xmax > ps2->xmin && ps->xmin < ps2->xmax && // overlap in X?
       
  1086 						ps->ymax > ps2->ymin && ps->ymin < ps2->ymax && // overlap in Y?
       
  1087 						ps->zmax > ps2->zmin && ps->zmin < ps2->zmax) { // overlap in Z?
       
  1088 					/* Use X+Y+Z as the sorting order, so sprites closer to the bottom of
       
  1089 					 * the screen and with higher Z elevation, are drawn in front.
       
  1090 					 * Here X,Y,Z are the coordinates of the "center of mass" of the sprite,
       
  1091 					 * i.e. X=(left+right)/2, etc.
       
  1092 					 * However, since we only care about order, don't actually divide / 2
       
  1093 					 */
       
  1094 					if (ps->xmin + ps->xmax + ps->ymin + ps->ymax + ps->zmin + ps->zmax <=
       
  1095 							ps2->xmin + ps2->xmax + ps2->ymin + ps2->ymax + ps2->zmin + ps2->zmax) {
       
  1096 						continue;
       
  1097 					}
       
  1098 				} else {
       
  1099 					if (ps->xmax < ps2->xmin ||
       
  1100 							ps->ymax < ps2->ymin ||
       
  1101 							ps->zmax < ps2->zmin || (
       
  1102 								ps->xmin < ps2->xmax &&
       
  1103 								ps->ymin < ps2->ymax &&
       
  1104 								ps->zmin < ps2->zmax
       
  1105 							)) {
       
  1106 						continue;
       
  1107 					}
       
  1108 				}
       
  1109 
       
  1110 				// Swap the two sprites ps and ps2 using bubble-sort algorithm.
       
  1111 				psd3 = psd;
       
  1112 				do {
       
  1113 					ParentSpriteToDraw* temp = *psd3;
       
  1114 					*psd3 = ps2;
       
  1115 					ps2 = temp;
       
  1116 
       
  1117 					psd3++;
       
  1118 				} while (psd3 <= psd2);
       
  1119 			}
       
  1120 		} else {
       
  1121 			psd++;
       
  1122 		}
       
  1123 	}
       
  1124 }
       
  1125 
       
  1126 static void ViewportDrawParentSprites(ParentSpriteToDraw *psd[])
       
  1127 {
       
  1128 	for (; *psd != NULL; psd++) {
       
  1129 		const ParentSpriteToDraw* ps = *psd;
       
  1130 		Point pt = RemapCoords(ps->xmin, ps->ymin, ps->zmin);
       
  1131 		const ChildScreenSpriteToDraw* cs;
       
  1132 
       
  1133 		DrawSprite(ps->image, pt.x, pt.y);
       
  1134 
       
  1135 		for (cs = ps->child; cs != NULL; cs = cs->next) {
       
  1136 			DrawSprite(cs->image, ps->left + cs->x, ps->top + cs->y);
       
  1137 		}
       
  1138 	}
       
  1139 }
       
  1140 
       
  1141 static void ViewportDrawStrings(DrawPixelInfo *dpi, const StringSpriteToDraw *ss)
       
  1142 {
       
  1143 	DrawPixelInfo dp;
       
  1144 	byte zoom;
       
  1145 
       
  1146 	_cur_dpi = &dp;
       
  1147 	dp = *dpi;
       
  1148 
       
  1149 	zoom = dp.zoom;
       
  1150 	dp.zoom = 0;
       
  1151 
       
  1152 	dp.left >>= zoom;
       
  1153 	dp.top >>= zoom;
       
  1154 	dp.width >>= zoom;
       
  1155 	dp.height >>= zoom;
       
  1156 
       
  1157 	do {
       
  1158 		uint16 colour;
       
  1159 
       
  1160 		if (ss->width != 0) {
       
  1161 			int x = (ss->x >> zoom) - 1;
       
  1162 			int y = (ss->y >> zoom) - 1;
       
  1163 			int bottom = y + 11;
       
  1164 			int w = ss->width;
       
  1165 
       
  1166 			if (w & 0x8000) {
       
  1167 				w &= ~0x8000;
       
  1168 				y--;
       
  1169 				bottom -= 6;
       
  1170 				w -= 3;
       
  1171 			}
       
  1172 
       
  1173 		/* Draw the rectangle if 'tranparent station signs' is off,
       
  1174 		 * or if we are drawing a general text sign (STR_2806) */
       
  1175 			if (!(_display_opt & DO_TRANS_SIGNS) || ss->string == STR_2806)
       
  1176 				DrawFrameRect(
       
  1177 					x, y, x + w, bottom, ss->color,
       
  1178 					(_display_opt & DO_TRANS_BUILDINGS) ? FR_TRANSPARENT : 0
       
  1179 				);
       
  1180 		}
       
  1181 
       
  1182 		SetDParam(0, ss->params[0]);
       
  1183 		SetDParam(1, ss->params[1]);
       
  1184 		/* if we didn't draw a rectangle, or if transparant building is on,
       
  1185 		 * draw the text in the color the rectangle would have */
       
  1186 		if ((
       
  1187 					(_display_opt & DO_TRANS_BUILDINGS) ||
       
  1188 					(_display_opt & DO_TRANS_SIGNS && ss->string != STR_2806)
       
  1189 				) && ss->width != 0) {
       
  1190 			/* Real colors need the IS_PALETTE_COLOR flag
       
  1191 			 * otherwise colors from _string_colormap are assumed. */
       
  1192 			colour = _colour_gradient[ss->color][6] | IS_PALETTE_COLOR;
       
  1193 		} else {
       
  1194 			colour = 16;
       
  1195 		}
       
  1196 		DrawString(
       
  1197 			ss->x >> zoom, (ss->y >> zoom) - (ss->width & 0x8000 ? 2 : 0),
       
  1198 			ss->string, colour
       
  1199 		);
       
  1200 
       
  1201 		ss = ss->next;
       
  1202 	} while (ss != NULL);
       
  1203 }
       
  1204 
       
  1205 void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom)
       
  1206 {
       
  1207 	ViewportDrawer vd;
       
  1208 	int mask;
       
  1209 	int x;
       
  1210 	int y;
       
  1211 	DrawPixelInfo *old_dpi;
       
  1212 
       
  1213 	byte mem[VIEWPORT_DRAW_MEM];
       
  1214 	ParentSpriteToDraw *parent_list[6144];
       
  1215 
       
  1216 	_cur_vd = &vd;
       
  1217 
       
  1218 	old_dpi = _cur_dpi;
       
  1219 	_cur_dpi = &vd.dpi;
       
  1220 
       
  1221 	vd.dpi.zoom = vp->zoom;
       
  1222 	mask = (-1) << vp->zoom;
       
  1223 
       
  1224 	vd.combine_sprites = 0;
       
  1225 
       
  1226 	vd.dpi.width = (right - left) & mask;
       
  1227 	vd.dpi.height = (bottom - top) & mask;
       
  1228 	vd.dpi.left = left & mask;
       
  1229 	vd.dpi.top = top & mask;
       
  1230 	vd.dpi.pitch = old_dpi->pitch;
       
  1231 
       
  1232 	x = ((vd.dpi.left - (vp->virtual_left&mask)) >> vp->zoom) + vp->left;
       
  1233 	y = ((vd.dpi.top - (vp->virtual_top&mask)) >> vp->zoom) + vp->top;
       
  1234 
       
  1235 	vd.dpi.dst_ptr = old_dpi->dst_ptr + x - old_dpi->left + (y - old_dpi->top) * old_dpi->pitch;
       
  1236 
       
  1237 	vd.parent_list = parent_list;
       
  1238 	vd.eof_parent_list = endof(parent_list);
       
  1239 	vd.spritelist_mem = mem;
       
  1240 	vd.eof_spritelist_mem = endof(mem) - sizeof(LARGEST_SPRITELIST_STRUCT);
       
  1241 	vd.last_string = &vd.first_string;
       
  1242 	vd.first_string = NULL;
       
  1243 	vd.last_tile = &vd.first_tile;
       
  1244 	vd.first_tile = NULL;
       
  1245 
       
  1246 	ViewportAddLandscape();
       
  1247 	ViewportAddVehicles(&vd.dpi);
       
  1248 	DrawTextEffects(&vd.dpi);
       
  1249 
       
  1250 	ViewportAddTownNames(&vd.dpi);
       
  1251 	ViewportAddStationNames(&vd.dpi);
       
  1252 	ViewportAddSigns(&vd.dpi);
       
  1253 	ViewportAddWaypoints(&vd.dpi);
       
  1254 
       
  1255 	// This assert should never happen (because the length of the parent_list
       
  1256 	//  is checked)
       
  1257 	assert(vd.parent_list <= endof(parent_list));
       
  1258 
       
  1259 	if (vd.first_tile != NULL) ViewportDrawTileSprites(vd.first_tile);
       
  1260 
       
  1261 	/* null terminate parent sprite list */
       
  1262 	*vd.parent_list = NULL;
       
  1263 
       
  1264 	ViewportSortParentSprites(parent_list);
       
  1265 	ViewportDrawParentSprites(parent_list);
       
  1266 
       
  1267 	if (vd.first_string != NULL) ViewportDrawStrings(&vd.dpi, vd.first_string);
       
  1268 
       
  1269 	_cur_dpi = old_dpi;
       
  1270 }
       
  1271 
       
  1272 // Make sure we don't draw a too big area at a time.
       
  1273 // If we do, the sprite memory will overflow.
       
  1274 static void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, int bottom)
       
  1275 {
       
  1276 	if (((bottom - top) * (right - left) << vp->zoom) > 180000) {
       
  1277 		if ((bottom - top) > (right - left)) {
       
  1278 			int t = (top + bottom) >> 1;
       
  1279 			ViewportDrawChk(vp, left, top, right, t);
       
  1280 			ViewportDrawChk(vp, left, t, right, bottom);
       
  1281 		} else {
       
  1282 			int t = (left + right) >> 1;
       
  1283 			ViewportDrawChk(vp, left, top, t, bottom);
       
  1284 			ViewportDrawChk(vp, t, top, right, bottom);
       
  1285 		}
       
  1286 	} else {
       
  1287 		ViewportDoDraw(vp,
       
  1288 			((left - vp->left) << vp->zoom) + vp->virtual_left,
       
  1289 			((top - vp->top) << vp->zoom) + vp->virtual_top,
       
  1290 			((right - vp->left) << vp->zoom) + vp->virtual_left,
       
  1291 			((bottom - vp->top) << vp->zoom) + vp->virtual_top
       
  1292 		);
       
  1293 	}
       
  1294 }
       
  1295 
       
  1296 static inline void ViewportDraw(const ViewPort *vp, int left, int top, int right, int bottom)
       
  1297 {
       
  1298 	if (right <= vp->left || bottom <= vp->top) return;
       
  1299 
       
  1300 	if (left >= vp->left + vp->width) return;
       
  1301 
       
  1302 	if (left < vp->left) left = vp->left;
       
  1303 	if (right > vp->left + vp->width) right = vp->left + vp->width;
       
  1304 
       
  1305 	if (top >= vp->top + vp->height) return;
       
  1306 
       
  1307 	if (top < vp->top) top = vp->top;
       
  1308 	if (bottom > vp->top + vp->height) bottom = vp->top + vp->height;
       
  1309 
       
  1310 	ViewportDrawChk(vp, left, top, right, bottom);
       
  1311 }
       
  1312 
       
  1313 void DrawWindowViewport(const Window *w)
       
  1314 {
       
  1315 	DrawPixelInfo *dpi = _cur_dpi;
       
  1316 
       
  1317 	dpi->left += w->left;
       
  1318 	dpi->top += w->top;
       
  1319 
       
  1320 	ViewportDraw(w->viewport, dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height);
       
  1321 
       
  1322 	dpi->left -= w->left;
       
  1323 	dpi->top -= w->top;
       
  1324 }
       
  1325 
       
  1326 void UpdateViewportPosition(Window *w)
       
  1327 {
       
  1328 	const ViewPort *vp = w->viewport;
       
  1329 
       
  1330 	if (WP(w, vp_d).follow_vehicle != INVALID_VEHICLE) {
       
  1331 		const Vehicle* veh = GetVehicle(WP(w,vp_d).follow_vehicle);
       
  1332 		Point pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
       
  1333 
       
  1334 		SetViewportPosition(w, pt.x, pt.y);
       
  1335 	} else {
       
  1336 		int x;
       
  1337 		int y;
       
  1338 		int vx;
       
  1339 		int vy;
       
  1340 
       
  1341 		// Center of the viewport is hot spot
       
  1342 		x = WP(w,vp_d).scrollpos_x + vp->virtual_width / 2;
       
  1343 		y = WP(w,vp_d).scrollpos_y + vp->virtual_height / 2;
       
  1344 		// Convert viewport coordinates to map coordinates
       
  1345 		// Calculation is scaled by 4 to avoid rounding errors
       
  1346 		vx = -x + y * 2;
       
  1347 		vy =  x + y * 2;
       
  1348 		// clamp to size of map
       
  1349 		vx = clamp(vx, 0 * 4, MapMaxX() * TILE_SIZE * 4);
       
  1350 		vy = clamp(vy, 0 * 4, MapMaxY() * TILE_SIZE * 4);
       
  1351 		// Convert map coordinates to viewport coordinates
       
  1352 		x = (-vx + vy) / 2;
       
  1353 		y = ( vx + vy) / 4;
       
  1354 		// Set position
       
  1355 		WP(w, vp_d).scrollpos_x = x - vp->virtual_width / 2;
       
  1356 		WP(w, vp_d).scrollpos_y = y - vp->virtual_height / 2;
       
  1357 
       
  1358 		SetViewportPosition(w, WP(w, vp_d).scrollpos_x, WP(w, vp_d).scrollpos_y);
       
  1359 	}
       
  1360 }
       
  1361 
       
  1362 static void MarkViewportDirty(const ViewPort *vp, int left, int top, int right, int bottom)
       
  1363 {
       
  1364 	right -= vp->virtual_left;
       
  1365 	if (right <= 0) return;
       
  1366 
       
  1367 	bottom -= vp->virtual_top;
       
  1368 	if (bottom <= 0) return;
       
  1369 
       
  1370 	left = max(0, left - vp->virtual_left);
       
  1371 
       
  1372 	if (left >= vp->virtual_width) return;
       
  1373 
       
  1374 	top = max(0, top - vp->virtual_top);
       
  1375 
       
  1376 	if (top >= vp->virtual_height) return;
       
  1377 
       
  1378 	SetDirtyBlocks(
       
  1379 		(left >> vp->zoom) + vp->left,
       
  1380 		(top >> vp->zoom) + vp->top,
       
  1381 		(right >> vp->zoom) + vp->left,
       
  1382 		(bottom >> vp->zoom) + vp->top
       
  1383 	);
       
  1384 }
       
  1385 
       
  1386 void MarkAllViewportsDirty(int left, int top, int right, int bottom)
       
  1387 {
       
  1388 	const ViewPort *vp = _viewports;
       
  1389 	uint32 act = _active_viewports;
       
  1390 	do {
       
  1391 		if (act & 1) {
       
  1392 			assert(vp->width != 0);
       
  1393 			MarkViewportDirty(vp, left, top, right, bottom);
       
  1394 		}
       
  1395 	} while (vp++,act>>=1);
       
  1396 }
       
  1397 
       
  1398 void MarkTileDirtyByTile(TileIndex tile)
       
  1399 {
       
  1400 	Point pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, GetTileZ(tile));
       
  1401 	MarkAllViewportsDirty(
       
  1402 		pt.x - 31,
       
  1403 		pt.y - 122,
       
  1404 		pt.x - 31 + 67,
       
  1405 		pt.y - 122 + 154
       
  1406 	);
       
  1407 }
       
  1408 
       
  1409 void MarkTileDirty(int x, int y)
       
  1410 {
       
  1411 	uint z = 0;
       
  1412 	Point pt;
       
  1413 
       
  1414 	if (IS_INT_INSIDE(x, 0, MapSizeX() * TILE_SIZE) &&
       
  1415 			IS_INT_INSIDE(y, 0, MapSizeY() * TILE_SIZE))
       
  1416 		z = GetTileZ(TileVirtXY(x, y));
       
  1417 	pt = RemapCoords(x, y, z);
       
  1418 
       
  1419 	MarkAllViewportsDirty(
       
  1420 		pt.x - 31,
       
  1421 		pt.y - 122,
       
  1422 		pt.x - 31 + 67,
       
  1423 		pt.y - 122 + 154
       
  1424 	);
       
  1425 }
       
  1426 
       
  1427 static void SetSelectionTilesDirty(void)
       
  1428 {
       
  1429 	int y_size, x_size;
       
  1430 	int x = _thd.pos.x;
       
  1431 	int y = _thd.pos.y;
       
  1432 
       
  1433 	x_size = _thd.size.x;
       
  1434 	y_size = _thd.size.y;
       
  1435 
       
  1436 	if (_thd.outersize.x) {
       
  1437 		x_size += _thd.outersize.x;
       
  1438 		x += _thd.offs.x;
       
  1439 		y_size += _thd.outersize.y;
       
  1440 		y += _thd.offs.y;
       
  1441 	}
       
  1442 
       
  1443 	assert(x_size > 0);
       
  1444 	assert(y_size > 0);
       
  1445 
       
  1446 	x_size += x;
       
  1447 	y_size += y;
       
  1448 
       
  1449 	do {
       
  1450 		int y_bk = y;
       
  1451 		do {
       
  1452 			MarkTileDirty(x, y);
       
  1453 		} while ( (y += TILE_SIZE) != y_size);
       
  1454 		y = y_bk;
       
  1455 	} while ( (x += TILE_SIZE) != x_size);
       
  1456 }
       
  1457 
       
  1458 
       
  1459 void SetSelectionRed(bool b)
       
  1460 {
       
  1461 	_thd.make_square_red = b;
       
  1462 	SetSelectionTilesDirty();
       
  1463 }
       
  1464 
       
  1465 
       
  1466 static bool CheckClickOnTown(const ViewPort *vp, int x, int y)
       
  1467 {
       
  1468 	const Town *t;
       
  1469 
       
  1470 	if (!(_display_opt & DO_SHOW_TOWN_NAMES)) return false;
       
  1471 
       
  1472 	switch (vp->zoom) {
       
  1473 		case 0:
       
  1474 			x = x - vp->left + vp->virtual_left;
       
  1475 			y = y - vp->top  + vp->virtual_top;
       
  1476 			FOR_ALL_TOWNS(t) {
       
  1477 				if (y >= t->sign.top &&
       
  1478 						y < t->sign.top + 12 &&
       
  1479 						x >= t->sign.left &&
       
  1480 						x < t->sign.left + t->sign.width_1) {
       
  1481 					ShowTownViewWindow(t->index);
       
  1482 					return true;
       
  1483 				}
       
  1484 			}
       
  1485 			break;
       
  1486 
       
  1487 		case 1:
       
  1488 			x = (x - vp->left + 1) * 2 + vp->virtual_left;
       
  1489 			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
       
  1490 			FOR_ALL_TOWNS(t) {
       
  1491 				if (y >= t->sign.top &&
       
  1492 						y < t->sign.top + 24 &&
       
  1493 						x >= t->sign.left &&
       
  1494 						x < t->sign.left + t->sign.width_1 * 2) {
       
  1495 					ShowTownViewWindow(t->index);
       
  1496 					return true;
       
  1497 				}
       
  1498 			}
       
  1499 			break;
       
  1500 
       
  1501 		default:
       
  1502 			x = (x - vp->left + 3) * 4 + vp->virtual_left;
       
  1503 			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
       
  1504 			FOR_ALL_TOWNS(t) {
       
  1505 				if (y >= t->sign.top &&
       
  1506 						y < t->sign.top + 24 &&
       
  1507 						x >= t->sign.left &&
       
  1508 						x < t->sign.left + t->sign.width_2 * 4) {
       
  1509 					ShowTownViewWindow(t->index);
       
  1510 					return true;
       
  1511 				}
       
  1512 			}
       
  1513 			break;
       
  1514 	}
       
  1515 
       
  1516 	return false;
       
  1517 }
       
  1518 
       
  1519 
       
  1520 static bool CheckClickOnStation(const ViewPort *vp, int x, int y)
       
  1521 {
       
  1522 	const Station *st;
       
  1523 
       
  1524 	if (!(_display_opt & DO_SHOW_STATION_NAMES)) return false;
       
  1525 
       
  1526 	switch (vp->zoom) {
       
  1527 		case 0:
       
  1528 			x = x - vp->left + vp->virtual_left;
       
  1529 			y = y - vp->top  + vp->virtual_top;
       
  1530 			FOR_ALL_STATIONS(st) {
       
  1531 				if (y >= st->sign.top &&
       
  1532 						y < st->sign.top + 12 &&
       
  1533 						x >= st->sign.left &&
       
  1534 						x < st->sign.left + st->sign.width_1) {
       
  1535 					ShowStationViewWindow(st->index);
       
  1536 					return true;
       
  1537 				}
       
  1538 			}
       
  1539 			break;
       
  1540 
       
  1541 		case 1:
       
  1542 			x = (x - vp->left + 1) * 2 + vp->virtual_left;
       
  1543 			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
       
  1544 			FOR_ALL_STATIONS(st) {
       
  1545 				if (y >= st->sign.top &&
       
  1546 						y < st->sign.top + 24 &&
       
  1547 						x >= st->sign.left &&
       
  1548 						x < st->sign.left + st->sign.width_1 * 2) {
       
  1549 					ShowStationViewWindow(st->index);
       
  1550 					return true;
       
  1551 				}
       
  1552 			}
       
  1553 			break;
       
  1554 
       
  1555 		default:
       
  1556 			x = (x - vp->left + 3) * 4 + vp->virtual_left;
       
  1557 			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
       
  1558 			FOR_ALL_STATIONS(st) {
       
  1559 				if (y >= st->sign.top &&
       
  1560 						y < st->sign.top + 24 &&
       
  1561 						x >= st->sign.left &&
       
  1562 						x < st->sign.left + st->sign.width_2 * 4) {
       
  1563 					ShowStationViewWindow(st->index);
       
  1564 					return true;
       
  1565 				}
       
  1566 			}
       
  1567 			break;
       
  1568 	}
       
  1569 
       
  1570 	return false;
       
  1571 }
       
  1572 
       
  1573 
       
  1574 static bool CheckClickOnSign(const ViewPort *vp, int x, int y)
       
  1575 {
       
  1576 	const Sign *si;
       
  1577 
       
  1578 	if (!(_display_opt & DO_SHOW_SIGNS)) return false;
       
  1579 
       
  1580 	switch (vp->zoom) {
       
  1581 		case 0:
       
  1582 			x = x - vp->left + vp->virtual_left;
       
  1583 			y = y - vp->top  + vp->virtual_top;
       
  1584 			FOR_ALL_SIGNS(si) {
       
  1585 				if (y >= si->sign.top &&
       
  1586 						y <  si->sign.top + 12 &&
       
  1587 						x >= si->sign.left &&
       
  1588 						x <  si->sign.left + si->sign.width_1) {
       
  1589 					ShowRenameSignWindow(si);
       
  1590 					return true;
       
  1591 				}
       
  1592 			}
       
  1593 			break;
       
  1594 
       
  1595 		case 1:
       
  1596 			x = (x - vp->left + 1) * 2 + vp->virtual_left;
       
  1597 			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
       
  1598 			FOR_ALL_SIGNS(si) {
       
  1599 				if (y >= si->sign.top &&
       
  1600 						y <  si->sign.top + 24 &&
       
  1601 						x >= si->sign.left &&
       
  1602 						x <  si->sign.left + si->sign.width_1 * 2) {
       
  1603 					ShowRenameSignWindow(si);
       
  1604 					return true;
       
  1605 				}
       
  1606 			}
       
  1607 			break;
       
  1608 
       
  1609 		default:
       
  1610 			x = (x - vp->left + 3) * 4 + vp->virtual_left;
       
  1611 			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
       
  1612 			FOR_ALL_SIGNS(si) {
       
  1613 				if (y >= si->sign.top &&
       
  1614 						y <  si->sign.top + 24 &&
       
  1615 						x >= si->sign.left &&
       
  1616 						x <  si->sign.left + si->sign.width_2 * 4) {
       
  1617 					ShowRenameSignWindow(si);
       
  1618 					return true;
       
  1619 				}
       
  1620 			}
       
  1621 			break;
       
  1622 	}
       
  1623 
       
  1624 	return false;
       
  1625 }
       
  1626 
       
  1627 
       
  1628 static bool CheckClickOnWaypoint(const ViewPort *vp, int x, int y)
       
  1629 {
       
  1630 	const Waypoint *wp;
       
  1631 
       
  1632 	if (!(_display_opt & DO_WAYPOINTS)) return false;
       
  1633 
       
  1634 	switch (vp->zoom) {
       
  1635 		case 0:
       
  1636 			x = x - vp->left + vp->virtual_left;
       
  1637 			y = y - vp->top  + vp->virtual_top;
       
  1638 			FOR_ALL_WAYPOINTS(wp) {
       
  1639 				if (y >= wp->sign.top &&
       
  1640 						y < wp->sign.top + 12 &&
       
  1641 						x >= wp->sign.left &&
       
  1642 						x < wp->sign.left + wp->sign.width_1) {
       
  1643 					ShowRenameWaypointWindow(wp);
       
  1644 					return true;
       
  1645 				}
       
  1646 			}
       
  1647 			break;
       
  1648 
       
  1649 		case 1:
       
  1650 			x = (x - vp->left + 1) * 2 + vp->virtual_left;
       
  1651 			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
       
  1652 			FOR_ALL_WAYPOINTS(wp) {
       
  1653 				if (y >= wp->sign.top &&
       
  1654 						y < wp->sign.top + 24 &&
       
  1655 						x >= wp->sign.left &&
       
  1656 						x < wp->sign.left + wp->sign.width_1 * 2) {
       
  1657 					ShowRenameWaypointWindow(wp);
       
  1658 					return true;
       
  1659 				}
       
  1660 			}
       
  1661 			break;
       
  1662 
       
  1663 		default:
       
  1664 			x = (x - vp->left + 3) * 4 + vp->virtual_left;
       
  1665 			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
       
  1666 			FOR_ALL_WAYPOINTS(wp) {
       
  1667 				if (y >= wp->sign.top &&
       
  1668 						y < wp->sign.top + 24 &&
       
  1669 						x >= wp->sign.left &&
       
  1670 						x < wp->sign.left + wp->sign.width_2 * 4) {
       
  1671 					ShowRenameWaypointWindow(wp);
       
  1672 					return true;
       
  1673 				}
       
  1674 			}
       
  1675 			break;
       
  1676 	}
       
  1677 
       
  1678 	return false;
       
  1679 }
       
  1680 
       
  1681 
       
  1682 static void CheckClickOnLandscape(const ViewPort *vp, int x, int y)
       
  1683 {
       
  1684 	Point pt = TranslateXYToTileCoord(vp, x, y);
       
  1685 
       
  1686 	if (pt.x != -1) ClickTile(TileVirtXY(pt.x, pt.y));
       
  1687 }
       
  1688 
       
  1689 
       
  1690 static void SafeShowTrainViewWindow(const Vehicle* v)
       
  1691 {
       
  1692 	if (!IsFrontEngine(v)) v = GetFirstVehicleInChain(v);
       
  1693 	ShowTrainViewWindow(v);
       
  1694 }
       
  1695 
       
  1696 static void Nop(const Vehicle *v) {}
       
  1697 
       
  1698 typedef void OnVehicleClickProc(const Vehicle *v);
       
  1699 static OnVehicleClickProc* const _on_vehicle_click_proc[] = {
       
  1700 	SafeShowTrainViewWindow,
       
  1701 	ShowRoadVehViewWindow,
       
  1702 	ShowShipViewWindow,
       
  1703 	ShowAircraftViewWindow,
       
  1704 	Nop, // Special vehicles
       
  1705 	Nop  // Disaster vehicles
       
  1706 };
       
  1707 
       
  1708 void HandleViewportClicked(const ViewPort *vp, int x, int y)
       
  1709 {
       
  1710 	const Vehicle *v;
       
  1711 
       
  1712 	if (CheckClickOnTown(vp, x, y)) return;
       
  1713 	if (CheckClickOnStation(vp, x, y)) return;
       
  1714 	if (CheckClickOnSign(vp, x, y)) return;
       
  1715 	if (CheckClickOnWaypoint(vp, x, y)) return;
       
  1716 	CheckClickOnLandscape(vp, x, y);
       
  1717 
       
  1718 	v = CheckClickOnVehicle(vp, x, y);
       
  1719 	if (v != NULL) {
       
  1720 		DEBUG(misc, 2, "Vehicle %d (index %d) at %p", v->unitnumber, v->index, v);
       
  1721 		_on_vehicle_click_proc[v->type - 0x10](v);
       
  1722 	}
       
  1723 }
       
  1724 
       
  1725 Vehicle *CheckMouseOverVehicle(void)
       
  1726 {
       
  1727 	const Window *w;
       
  1728 	const ViewPort *vp;
       
  1729 
       
  1730 	int x = _cursor.pos.x;
       
  1731 	int y = _cursor.pos.y;
       
  1732 
       
  1733 	w = FindWindowFromPt(x, y);
       
  1734 	if (w == NULL) return NULL;
       
  1735 
       
  1736 	vp = IsPtInWindowViewport(w, x, y);
       
  1737 	return (vp != NULL) ? CheckClickOnVehicle(vp, x, y) : NULL;
       
  1738 }
       
  1739 
       
  1740 
       
  1741 
       
  1742 void PlaceObject(void)
       
  1743 {
       
  1744 	Point pt;
       
  1745 	Window *w;
       
  1746 
       
  1747 	pt = GetTileBelowCursor();
       
  1748 	if (pt.x == -1) return;
       
  1749 
       
  1750 	if (_thd.place_mode == VHM_POINT) {
       
  1751 		pt.x += 8;
       
  1752 		pt.y += 8;
       
  1753 	}
       
  1754 
       
  1755 	_tile_fract_coords.x = pt.x & 0xF;
       
  1756 	_tile_fract_coords.y = pt.y & 0xF;
       
  1757 
       
  1758 	w = GetCallbackWnd();
       
  1759 	if (w != NULL) {
       
  1760 		WindowEvent e;
       
  1761 
       
  1762 		e.event = WE_PLACE_OBJ;
       
  1763 		e.we.place.pt = pt;
       
  1764 		e.we.place.tile = TileVirtXY(pt.x, pt.y);
       
  1765 		w->wndproc(w, &e);
       
  1766 	}
       
  1767 }
       
  1768 
       
  1769 
       
  1770 /* scrolls the viewport in a window to a given location */
       
  1771 bool ScrollWindowTo(int x , int y, Window *w)
       
  1772 {
       
  1773 	Point pt;
       
  1774 
       
  1775 	pt = MapXYZToViewport(w->viewport, x, y, GetSlopeZ(x, y));
       
  1776 	WP(w, vp_d).follow_vehicle = INVALID_VEHICLE;
       
  1777 
       
  1778 	if (WP(w, vp_d).scrollpos_x == pt.x && WP(w, vp_d).scrollpos_y == pt.y)
       
  1779 		return false;
       
  1780 
       
  1781 	WP(w, vp_d).scrollpos_x = pt.x;
       
  1782 	WP(w, vp_d).scrollpos_y = pt.y;
       
  1783 	return true;
       
  1784 }
       
  1785 
       
  1786 
       
  1787 bool ScrollMainWindowTo(int x, int y)
       
  1788 {
       
  1789 	Window *w;
       
  1790 	bool res = ScrollWindowTo(x, y, FindWindowById(WC_MAIN_WINDOW, 0));
       
  1791 
       
  1792 	/* If a user scrolls to a tile (via what way what so ever) and already is on
       
  1793 	 *  that tile (e.g.: pressed twice), move the smallmap to that location,
       
  1794 	 *  so you directly see where you are on the smallmap. */
       
  1795 
       
  1796 	if (res) return res;
       
  1797 
       
  1798 	w = FindWindowById(WC_SMALLMAP, 0);
       
  1799 	if (w == NULL) return res;
       
  1800 
       
  1801 	SmallMapCenterOnCurrentPos(w);
       
  1802 
       
  1803 	return res;
       
  1804 }
       
  1805 
       
  1806 
       
  1807 bool ScrollMainWindowToTile(TileIndex tile)
       
  1808 {
       
  1809 	return ScrollMainWindowTo(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2);
       
  1810 }
       
  1811 
       
  1812 void SetRedErrorSquare(TileIndex tile)
       
  1813 {
       
  1814 	TileIndex old;
       
  1815 
       
  1816 	old = _thd.redsq;
       
  1817 	_thd.redsq = tile;
       
  1818 
       
  1819 	if (tile != old) {
       
  1820 		if (tile != 0) MarkTileDirtyByTile(tile);
       
  1821 		if (old  != 0) MarkTileDirtyByTile(old);
       
  1822 	}
       
  1823 }
       
  1824 
       
  1825 void SetTileSelectSize(int w, int h)
       
  1826 {
       
  1827 	_thd.new_size.x = w * TILE_SIZE;
       
  1828 	_thd.new_size.y = h * TILE_SIZE;
       
  1829 	_thd.new_outersize.x = 0;
       
  1830 	_thd.new_outersize.y = 0;
       
  1831 }
       
  1832 
       
  1833 void SetTileSelectBigSize(int ox, int oy, int sx, int sy)
       
  1834 {
       
  1835 	_thd.offs.x = ox * TILE_SIZE;
       
  1836 	_thd.offs.y = oy * TILE_SIZE;
       
  1837 	_thd.new_outersize.x = sx * TILE_SIZE;
       
  1838 	_thd.new_outersize.y = sy * TILE_SIZE;
       
  1839 }
       
  1840 
       
  1841 /* returns the best autorail highlight type from map coordinates */
       
  1842 static byte GetAutorailHT(int x, int y)
       
  1843 {
       
  1844 	return HT_RAIL | _AutorailPiece[x & 0xF][y & 0xF];
       
  1845 }
       
  1846 
       
  1847 // called regular to update tile highlighting in all cases
       
  1848 void UpdateTileSelection(void)
       
  1849 {
       
  1850 	int x1;
       
  1851 	int y1;
       
  1852 
       
  1853 	_thd.new_drawstyle = 0;
       
  1854 
       
  1855 	if (_thd.place_mode == VHM_SPECIAL) {
       
  1856 		x1 = _thd.selend.x;
       
  1857 		y1 = _thd.selend.y;
       
  1858 		if (x1 != -1) {
       
  1859 			int x2 = _thd.selstart.x;
       
  1860 			int y2 = _thd.selstart.y;
       
  1861 			x1 &= ~0xF;
       
  1862 			y1 &= ~0xF;
       
  1863 
       
  1864 			if (x1 >= x2) intswap(x1,x2);
       
  1865 			if (y1 >= y2) intswap(y1,y2);
       
  1866 			_thd.new_pos.x = x1;
       
  1867 			_thd.new_pos.y = y1;
       
  1868 			_thd.new_size.x = x2 - x1 + TILE_SIZE;
       
  1869 			_thd.new_size.y = y2 - y1 + TILE_SIZE;
       
  1870 			_thd.new_drawstyle = _thd.next_drawstyle;
       
  1871 		}
       
  1872 	} else if (_thd.place_mode != VHM_NONE) {
       
  1873 		Point pt = GetTileBelowCursor();
       
  1874 		x1 = pt.x;
       
  1875 		y1 = pt.y;
       
  1876 		if (x1 != -1) {
       
  1877 			switch (_thd.place_mode) {
       
  1878 				case VHM_RECT:
       
  1879 					_thd.new_drawstyle = HT_RECT;
       
  1880 					break;
       
  1881 				case VHM_POINT:
       
  1882 					_thd.new_drawstyle = HT_POINT;
       
  1883 					x1 += 8;
       
  1884 					y1 += 8;
       
  1885 					break;
       
  1886 				case VHM_RAIL:
       
  1887 					_thd.new_drawstyle = GetAutorailHT(pt.x, pt.y); // draw one highlighted tile
       
  1888 			}
       
  1889 			_thd.new_pos.x = x1 & ~0xF;
       
  1890 			_thd.new_pos.y = y1 & ~0xF;
       
  1891 		}
       
  1892 	}
       
  1893 
       
  1894 	// redraw selection
       
  1895 	if (_thd.drawstyle != _thd.new_drawstyle ||
       
  1896 			_thd.pos.x != _thd.new_pos.x || _thd.pos.y != _thd.new_pos.y ||
       
  1897 			_thd.size.x != _thd.new_size.x || _thd.size.y != _thd.new_size.y ||
       
  1898 	    _thd.outersize.x != _thd.new_outersize.x ||
       
  1899 	    _thd.outersize.y != _thd.new_outersize.y) {
       
  1900 		// clear the old selection?
       
  1901 		if (_thd.drawstyle) SetSelectionTilesDirty();
       
  1902 
       
  1903 		_thd.drawstyle = _thd.new_drawstyle;
       
  1904 		_thd.pos = _thd.new_pos;
       
  1905 		_thd.size = _thd.new_size;
       
  1906 		_thd.outersize = _thd.new_outersize;
       
  1907 		_thd.dirty = 0xff;
       
  1908 
       
  1909 		// draw the new selection?
       
  1910 		if (_thd.new_drawstyle) SetSelectionTilesDirty();
       
  1911 	}
       
  1912 }
       
  1913 
       
  1914 // highlighting tiles while only going over them with the mouse
       
  1915 void VpStartPlaceSizing(TileIndex tile, int user)
       
  1916 {
       
  1917 	_thd.userdata = user;
       
  1918 	_thd.selend.x = TileX(tile) * TILE_SIZE;
       
  1919 	_thd.selstart.x = TileX(tile) * TILE_SIZE;
       
  1920 	_thd.selend.y = TileY(tile) * TILE_SIZE;
       
  1921 	_thd.selstart.y = TileY(tile) * TILE_SIZE;
       
  1922 	if (_thd.place_mode == VHM_RECT) {
       
  1923 		_thd.place_mode = VHM_SPECIAL;
       
  1924 		_thd.next_drawstyle = HT_RECT;
       
  1925 	} else if (_thd.place_mode == VHM_RAIL) { // autorail one piece
       
  1926 		_thd.place_mode = VHM_SPECIAL;
       
  1927 		_thd.next_drawstyle = _thd.drawstyle;
       
  1928 	} else {
       
  1929 		_thd.place_mode = VHM_SPECIAL;
       
  1930 		_thd.next_drawstyle = HT_POINT;
       
  1931 	}
       
  1932 	_special_mouse_mode = WSM_SIZING;
       
  1933 }
       
  1934 
       
  1935 void VpSetPlaceSizingLimit(int limit)
       
  1936 {
       
  1937 	_thd.sizelimit = limit;
       
  1938 }
       
  1939 
       
  1940 /**
       
  1941 * Highlights all tiles between a set of two tiles. Used in dock and tunnel placement
       
  1942 * @param from TileIndex of the first tile to highlight
       
  1943 * @param to TileIndex of the last tile to highlight */
       
  1944 void VpSetPresizeRange(TileIndex from, TileIndex to)
       
  1945 {
       
  1946 	uint distance = DistanceManhattan(from, to) + 1;
       
  1947 
       
  1948 	_thd.selend.x = TileX(to) * TILE_SIZE;
       
  1949 	_thd.selend.y = TileY(to) * TILE_SIZE;
       
  1950 	_thd.selstart.x = TileX(from) * TILE_SIZE;
       
  1951 	_thd.selstart.y = TileY(from) * TILE_SIZE;
       
  1952 	_thd.next_drawstyle = HT_RECT;
       
  1953 
       
  1954 	/* show measurement only if there is any length to speak of */
       
  1955 	if (distance > 1) GuiShowTooltipsWithArgs(STR_MEASURE_LENGTH, 1, &distance);
       
  1956 }
       
  1957 
       
  1958 static void VpStartPreSizing(void)
       
  1959 {
       
  1960 	_thd.selend.x = -1;
       
  1961 	_special_mouse_mode = WSM_PRESIZE;
       
  1962 }
       
  1963 
       
  1964 /* returns information about the 2x1 piece to be build.
       
  1965  * The lower bits (0-3) are the track type. */
       
  1966 static byte Check2x1AutoRail(int mode)
       
  1967 {
       
  1968 	int fxpy = _tile_fract_coords.x + _tile_fract_coords.y;
       
  1969 	int sxpy = (_thd.selend.x & 0xF) + (_thd.selend.y & 0xF);
       
  1970 	int fxmy = _tile_fract_coords.x - _tile_fract_coords.y;
       
  1971 	int sxmy = (_thd.selend.x & 0xF) - (_thd.selend.y & 0xF);
       
  1972 
       
  1973 	switch (mode) {
       
  1974 	case 0: // end piece is lower right
       
  1975 		if (fxpy >= 20 && sxpy <= 12) { /*SwapSelection(); DoRailroadTrack(0); */return 3; }
       
  1976 		if (fxmy < -3 && sxmy > 3) {/* DoRailroadTrack(0); */return 5; }
       
  1977 		return 1;
       
  1978 
       
  1979 	case 1:
       
  1980 		if (fxmy > 3 && sxmy < -3) { /*SwapSelection(); DoRailroadTrack(0); */return 4; }
       
  1981 		if (fxpy <= 12 && sxpy >= 20) { /*DoRailroadTrack(0); */return 2; }
       
  1982 		return 1;
       
  1983 
       
  1984 	case 2:
       
  1985 		if (fxmy > 3 && sxmy < -3) { /*DoRailroadTrack(3);*/ return 4; }
       
  1986 		if (fxpy >= 20 && sxpy <= 12) { /*SwapSelection(); DoRailroadTrack(0); */return 3; }
       
  1987 		return 0;
       
  1988 
       
  1989 	case 3:
       
  1990 		if (fxmy < -3 && sxmy > 3) { /*SwapSelection(); DoRailroadTrack(3);*/ return 5; }
       
  1991 		if (fxpy <= 12 && sxpy >= 20) { /*DoRailroadTrack(0); */return 2; }
       
  1992 		return 0;
       
  1993 	}
       
  1994 
       
  1995 	return 0; // avoids compiler warnings
       
  1996 }
       
  1997 
       
  1998 /** Check if the direction of start and end tile should be swapped based on
       
  1999  * the dragging-style. Default directions are:
       
  2000  * in the case of a line (HT_RAIL, HT_LINE):  DIR_NE, DIR_NW, DIR_N, DIR_E
       
  2001  * in the case of a rect (HT_RECT, HT_POINT): DIR_S, DIR_E
       
  2002  * For example dragging a rectangle area from south to north should be swapped to
       
  2003  * north-south (DIR_S) to obtain the same results with less code. This is what
       
  2004  * the return value signifies.
       
  2005  * @param style HighLightStyle dragging style
       
  2006  * @param start_tile, end_tile start and end tile of drag
       
  2007  * @param boolean value which when true means start/end should be swapped */
       
  2008 static bool SwapDirection(HighLightStyle style, TileIndex start_tile, TileIndex end_tile)
       
  2009 {
       
  2010 	uint start_x = TileX(start_tile);
       
  2011 	uint start_y = TileY(start_tile);
       
  2012 	uint end_x = TileX(end_tile);
       
  2013 	uint end_y = TileY(end_tile);
       
  2014 
       
  2015 	switch (style & HT_DRAG_MASK) {
       
  2016 		case HT_RAIL:
       
  2017 		case HT_LINE: return (end_x > start_x || (end_x == start_x && end_y > start_y));
       
  2018 
       
  2019 		case HT_RECT:
       
  2020 		case HT_POINT: return (end_x != start_x && end_y < start_y);
       
  2021 		default: NOT_REACHED();
       
  2022 	}
       
  2023 
       
  2024 	return false;
       
  2025 }
       
  2026 
       
  2027 /** Calculates height difference between one tile and another
       
  2028 * Multiplies the result to suit the standard given by minimap - 50 meters high
       
  2029 * To correctly get the height difference we need the direction we are dragging
       
  2030 * in, as well as with what kind of tool we are dragging. For example a horizontal
       
  2031 * autorail tool that starts in bottom and ends at the top of a tile will need the
       
  2032 * maximum of SW,S and SE,N corners respectively. This is handled by the lookup table below
       
  2033 * See _tileoffs_by_dir in map.c for the direction enums if you can't figure out
       
  2034 * the values yourself.
       
  2035 * @param style HightlightStyle of drag. This includes direction and style (autorail, rect, etc.)
       
  2036 * @param distance amount of tiles dragged, important for horizontal/vertical drags
       
  2037 *        ignored for others
       
  2038 * @param start_tile, end_tile start and end tile of drag operation
       
  2039 * @return height difference between two tiles. Tile measurement tool utilizes
       
  2040 * this value in its tooltips */
       
  2041 static int CalcHeightdiff(HighLightStyle style, uint distance, TileIndex start_tile, TileIndex end_tile)
       
  2042 {
       
  2043 	bool swap = SwapDirection(style, start_tile, end_tile);
       
  2044 	byte style_t;
       
  2045 	uint h0, h1, ht; // start heigth, end height, and temp variable
       
  2046 
       
  2047 	if (start_tile == end_tile) return 0;
       
  2048 	if (swap) swap_tile(&start_tile, &end_tile);
       
  2049 
       
  2050 	switch (style & HT_DRAG_MASK) {
       
  2051 		case HT_RECT: {
       
  2052 			static const TileIndexDiffC heightdiff_area_by_dir[] = {
       
  2053 				/* Start */ {1, 0}, /* Dragging east */ {0, 0}, /* Dragging south */
       
  2054 				/* End   */ {0, 1}, /* Dragging east */ {1, 1}  /* Dragging south */
       
  2055 			};
       
  2056 
       
  2057 			/* In the case of an area we can determine whether we were dragging south or
       
  2058 			 * east by checking the X-coordinates of the tiles */
       
  2059 			style_t = (byte)(TileX(end_tile) > TileX(start_tile));
       
  2060 			start_tile = TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_area_by_dir[style_t]));
       
  2061 			end_tile   = TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_area_by_dir[2 + style_t]));
       
  2062 		}
       
  2063 		/* Fallthrough */
       
  2064 		case HT_POINT:
       
  2065 			h0 = TileHeight(start_tile);
       
  2066 			h1 = TileHeight(end_tile);
       
  2067 			break;
       
  2068 		default: { /* All other types, this is mostly only line/autorail */
       
  2069 			static const HighLightStyle flip_style_direction[] = {
       
  2070 				HT_DIR_X, HT_DIR_Y, HT_DIR_HL, HT_DIR_HU, HT_DIR_VR, HT_DIR_VL
       
  2071 			};
       
  2072 			static const TileIndexDiffC heightdiff_line_by_dir[] = {
       
  2073 				/* Start */ {1, 0}, {1, 1}, /* HT_DIR_X  */ {0, 1}, {1, 1}, /* HT_DIR_Y  */
       
  2074 				/* Start */ {1, 0}, {0, 0}, /* HT_DIR_HU */ {1, 0}, {1, 1}, /* HT_DIR_HL */
       
  2075 				/* Start */ {1, 0}, {1, 1}, /* HT_DIR_VL */ {0, 1}, {1, 1}, /* HT_DIR_VR */
       
  2076 
       
  2077 				/* Start */ {0, 1}, {0, 0}, /* HT_DIR_X  */ {1, 0}, {0, 0}, /* HT_DIR_Y  */
       
  2078 				/* End   */ {0, 1}, {0, 0}, /* HT_DIR_HU */ {1, 1}, {0, 1}, /* HT_DIR_HL */
       
  2079 				/* End   */ {1, 0}, {0, 0}, /* HT_DIR_VL */ {0, 0}, {0, 1}, /* HT_DIR_VR */
       
  2080 			};
       
  2081 
       
  2082 			distance %= 2; // we're only interested if the distance is even or uneven
       
  2083 			style &= HT_DIR_MASK;
       
  2084 
       
  2085 			/* To handle autorail, we do some magic to be able to use a lookup table.
       
  2086 			 * Firstly if we drag the other way around, we switch start&end, and if needed
       
  2087 			 * also flip the drag-position. Eg if it was on the left, and the distance is even
       
  2088 			 * that means the end, which is now the start is on the right */
       
  2089 			if (swap && distance == 0) style = flip_style_direction[style];
       
  2090 
       
  2091 			/* Use lookup table for start-tile based on HighLightStyle direction */
       
  2092 			style_t = style * 2;
       
  2093 			assert(style_t < lengthof(heightdiff_line_by_dir) - 13);
       
  2094 			h0 = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t])));
       
  2095 			ht = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t + 1])));
       
  2096 			h0 = maxu(h0, ht);
       
  2097 
       
  2098 			/* Use lookup table for end-tile based on HighLightStyle direction
       
  2099 			 * flip around side (lower/upper, left/right) based on distance */
       
  2100 			if (distance == 0) style_t = flip_style_direction[style] * 2;
       
  2101 			assert(style_t < lengthof(heightdiff_line_by_dir) - 13);
       
  2102 			h1 = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t])));
       
  2103 			ht = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t + 1])));
       
  2104 			h1 = maxu(h1, ht);
       
  2105 		} break;
       
  2106 	}
       
  2107 
       
  2108 	if (swap) swap_uint32(&h0, &h1);
       
  2109 	/* Minimap shows height in intervals of 50 meters, let's do the same */
       
  2110 	return (int)(h1 - h0) * 50;
       
  2111 }
       
  2112 
       
  2113 static const StringID measure_strings_length[] = {STR_NULL, STR_MEASURE_LENGTH, STR_MEASURE_LENGTH_HEIGHTDIFF};
       
  2114 
       
  2115 // while dragging
       
  2116 static void CalcRaildirsDrawstyle(TileHighlightData *thd, int x, int y, int method)
       
  2117 {
       
  2118 	HighLightStyle b;
       
  2119 	uint w, h;
       
  2120 
       
  2121 	int dx = thd->selstart.x - (thd->selend.x & ~0xF);
       
  2122 	int dy = thd->selstart.y - (thd->selend.y & ~0xF);
       
  2123 	w = myabs(dx) + 16;
       
  2124 	h = myabs(dy) + 16;
       
  2125 
       
  2126 	if (TileVirtXY(thd->selstart.x, thd->selstart.y) == TileVirtXY(x, y)) { // check if we're only within one tile
       
  2127 		if (method == VPM_RAILDIRS) {
       
  2128 			b = GetAutorailHT(x, y);
       
  2129 		} else { // rect for autosignals on one tile
       
  2130 			b = HT_RECT;
       
  2131 		}
       
  2132 	} else if (h == 16) { // Is this in X direction?
       
  2133 		if (dx == 16) { // 2x1 special handling
       
  2134 			b = (Check2x1AutoRail(3)) | HT_LINE;
       
  2135 		} else if (dx == -16) {
       
  2136 			b = (Check2x1AutoRail(2)) | HT_LINE;
       
  2137 		} else {
       
  2138 			b = HT_LINE | HT_DIR_X;
       
  2139 		}
       
  2140 		y = thd->selstart.y;
       
  2141 	} else if (w == 16) { // Or Y direction?
       
  2142 		if (dy == 16) { // 2x1 special handling
       
  2143 			b = (Check2x1AutoRail(1)) | HT_LINE;
       
  2144 		} else if (dy == -16) { // 2x1 other direction
       
  2145 			b = (Check2x1AutoRail(0)) | HT_LINE;
       
  2146 		} else {
       
  2147 			b = HT_LINE | HT_DIR_Y;
       
  2148 		}
       
  2149 		x = thd->selstart.x;
       
  2150 	} else if (w > h * 2) { // still count as x dir?
       
  2151 		b = HT_LINE | HT_DIR_X;
       
  2152 		y = thd->selstart.y;
       
  2153 	} else if (h > w * 2) { // still count as y dir?
       
  2154 		b = HT_LINE | HT_DIR_Y;
       
  2155 		x = thd->selstart.x;
       
  2156 	} else { // complicated direction
       
  2157 		int d = w - h;
       
  2158 		thd->selend.x = thd->selend.x & ~0xF;
       
  2159 		thd->selend.y = thd->selend.y & ~0xF;
       
  2160 
       
  2161 		// four cases.
       
  2162 		if (x > thd->selstart.x) {
       
  2163 			if (y > thd->selstart.y) {
       
  2164 				// south
       
  2165 				if (d == 0) {
       
  2166 					b = (x & 0xF) > (y & 0xF) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
       
  2167 				} else if (d >= 0) {
       
  2168 					x = thd->selstart.x + h;
       
  2169 					b = HT_LINE | HT_DIR_VL;
       
  2170 					// return px == py || px == py + 16;
       
  2171 				} else {
       
  2172 					y = thd->selstart.y + w;
       
  2173 					b = HT_LINE | HT_DIR_VR;
       
  2174 				} // return px == py || px == py - 16;
       
  2175 			} else {
       
  2176 				// west
       
  2177 				if (d == 0) {
       
  2178 					b = (x & 0xF) + (y & 0xF) >= 0x10 ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
       
  2179 				} else if (d >= 0) {
       
  2180 					x = thd->selstart.x + h;
       
  2181 					b = HT_LINE | HT_DIR_HL;
       
  2182 				} else {
       
  2183 					y = thd->selstart.y - w;
       
  2184 					b = HT_LINE | HT_DIR_HU;
       
  2185 				}
       
  2186 			}
       
  2187 		} else {
       
  2188 			if (y > thd->selstart.y) {
       
  2189 				// east
       
  2190 				if (d == 0) {
       
  2191 					b = (x & 0xF) + (y & 0xF) >= 0x10 ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
       
  2192 				} else if (d >= 0) {
       
  2193 					x = thd->selstart.x - h;
       
  2194 					b = HT_LINE | HT_DIR_HU;
       
  2195 					// return px == -py || px == -py - 16;
       
  2196 				} else {
       
  2197 					y = thd->selstart.y + w;
       
  2198 					b = HT_LINE | HT_DIR_HL;
       
  2199 				} // return px == -py || px == -py + 16;
       
  2200 			} else {
       
  2201 				// north
       
  2202 				if (d == 0) {
       
  2203 					b = (x & 0xF) > (y & 0xF) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
       
  2204 				} else if (d >= 0) {
       
  2205 					x = thd->selstart.x - h;
       
  2206 					b = HT_LINE | HT_DIR_VR;
       
  2207 					// return px == py || px == py - 16;
       
  2208 				} else {
       
  2209 					y = thd->selstart.y - w;
       
  2210 					b = HT_LINE | HT_DIR_VL;
       
  2211 				} //return px == py || px == py + 16;
       
  2212 			}
       
  2213 		}
       
  2214 	}
       
  2215 
       
  2216 	if (_patches.measure_tooltip) {
       
  2217 		TileIndex t0 = TileVirtXY(thd->selstart.x, thd->selstart.y);
       
  2218 		TileIndex t1 = TileVirtXY(x, y);
       
  2219 		uint distance = DistanceManhattan(t0, t1) + 1;
       
  2220 		byte index = 0;
       
  2221 		uint params[2];
       
  2222 
       
  2223 		if (distance != 1) {
       
  2224 			int heightdiff = CalcHeightdiff(b, distance, t0, t1);
       
  2225 			/* If we are showing a tooltip for horizontal or vertical drags,
       
  2226 			 * 2 tiles have a length of 1. To bias towards the ceiling we add
       
  2227 			 * one before division. It feels more natural to count 3 lengths as 2 */
       
  2228 			if ((b & HT_DIR_MASK) != HT_DIR_X && (b & HT_DIR_MASK) != HT_DIR_Y) {
       
  2229 				distance = (distance + 1) / 2;
       
  2230 			}
       
  2231 
       
  2232 			params[index++] = distance;
       
  2233 			if (heightdiff != 0) params[index++] = heightdiff;
       
  2234 		}
       
  2235 
       
  2236 		GuiShowTooltipsWithArgs(measure_strings_length[index], index, params);
       
  2237 	}
       
  2238 
       
  2239 	thd->selend.x = x;
       
  2240 	thd->selend.y = y;
       
  2241 	thd->next_drawstyle = b;
       
  2242 }
       
  2243 
       
  2244 /**
       
  2245  * Selects tiles while dragging
       
  2246  * @param x X coordinate of end of selection
       
  2247  * @param y Y coordinate of end of selection
       
  2248  * @param method modifies the way tiles are selected. Possible
       
  2249  * methods are VPM_* in viewport.h */
       
  2250 void VpSelectTilesWithMethod(int x, int y, int method)
       
  2251 {
       
  2252 	int sx, sy;
       
  2253 	HighLightStyle style;
       
  2254 
       
  2255 	if (x == -1) {
       
  2256 		_thd.selend.x = -1;
       
  2257 		return;
       
  2258 	}
       
  2259 
       
  2260 	/* Special handling of drag in any (8-way) direction */
       
  2261 	if (method == VPM_RAILDIRS || method == VPM_SIGNALDIRS) {
       
  2262 		_thd.selend.x = x;
       
  2263 		_thd.selend.y = y;
       
  2264 		CalcRaildirsDrawstyle(&_thd, x, y, method);
       
  2265 		return;
       
  2266 	}
       
  2267 
       
  2268 	if (_thd.next_drawstyle == HT_POINT) {
       
  2269 		x += TILE_SIZE / 2;
       
  2270 		y += TILE_SIZE / 2;
       
  2271 	}
       
  2272 
       
  2273 	sx = _thd.selstart.x;
       
  2274 	sy = _thd.selstart.y;
       
  2275 
       
  2276 	switch (method) {
       
  2277 		case VPM_X_OR_Y: /* drag in X or Y direction */
       
  2278 			if (myabs(sy - y) < myabs(sx - x)) {
       
  2279 				y = sy;
       
  2280 				style = HT_DIR_X;
       
  2281 			} else {
       
  2282 				x = sx;
       
  2283 				style = HT_DIR_Y;
       
  2284 			}
       
  2285 			goto calc_heightdiff_single_direction;
       
  2286 		case VPM_FIX_X: /* drag in Y direction */
       
  2287 			x = sx;
       
  2288 			style = HT_DIR_Y;
       
  2289 			goto calc_heightdiff_single_direction;
       
  2290 		case VPM_FIX_Y: /* drag in X direction */
       
  2291 			y = sy;
       
  2292 			style = HT_DIR_X;
       
  2293 
       
  2294 calc_heightdiff_single_direction:;
       
  2295 			if (_patches.measure_tooltip) {
       
  2296 				TileIndex t0 = TileVirtXY(sx, sy);
       
  2297 				TileIndex t1 = TileVirtXY(x, y);
       
  2298 				uint distance = DistanceManhattan(t0, t1) + 1;
       
  2299 				byte index = 0;
       
  2300 				uint params[2];
       
  2301 
       
  2302 				if (distance != 1) {
       
  2303 					/* With current code passing a HT_LINE style to calculate the height
       
  2304 					 * difference is enough. However if/when a point-tool is created
       
  2305 					 * with this method, function should be called with new_style (below)
       
  2306 					 * instead of HT_LINE | style case HT_POINT is handled specially
       
  2307 					 * new_style := (_thd.next_drawstyle & HT_RECT) ? HT_LINE | style : _thd.next_drawstyle; */
       
  2308 					int heightdiff = CalcHeightdiff(HT_LINE | style, 0, t0, t1);
       
  2309 
       
  2310 					params[index++] = distance;
       
  2311 					if (heightdiff != 0) params[index++] = heightdiff;
       
  2312 				}
       
  2313 
       
  2314 				GuiShowTooltipsWithArgs(measure_strings_length[index], index, params);
       
  2315 			} break;
       
  2316 
       
  2317 		case VPM_X_AND_Y_LIMITED: { /* drag an X by Y constrained rect area */
       
  2318 			int limit = (_thd.sizelimit - 1) * TILE_SIZE;
       
  2319 			x = sx + clamp(x - sx, -limit, limit);
       
  2320 			y = sy + clamp(y - sy, -limit, limit);
       
  2321 			/* Fallthrough */
       
  2322 		case VPM_X_AND_Y: /* drag an X by Y area */
       
  2323 			if (_patches.measure_tooltip) {
       
  2324 				static const StringID measure_strings_area[] = {
       
  2325 					STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
       
  2326 				};
       
  2327 
       
  2328 				TileIndex t0 = TileVirtXY(sx, sy);
       
  2329 				TileIndex t1 = TileVirtXY(x, y);
       
  2330 				uint dx = abs(TileX(t0) - TileX(t1)) + 1;
       
  2331 				uint dy = abs(TileY(t0) - TileY(t1)) + 1;
       
  2332 				byte index = 0;
       
  2333 				uint params[3];
       
  2334 
       
  2335 				/* If dragging an area (eg dynamite tool) and it is actually a single
       
  2336 				 * row/column, change the type to 'line' to get proper calculation for height */
       
  2337 				style = _thd.next_drawstyle;
       
  2338 				if (style & HT_RECT) {
       
  2339 					if (dx == 1) {
       
  2340 						style = HT_LINE | HT_DIR_Y;
       
  2341 					} else if (dy == 1) {
       
  2342 						style = HT_LINE | HT_DIR_X;
       
  2343 					}
       
  2344 				}
       
  2345 
       
  2346 				if (dx != 1 || dy != 1) {
       
  2347 					int heightdiff = CalcHeightdiff(style, 0, t0, t1);
       
  2348 
       
  2349 					params[index++] = dx;
       
  2350 					params[index++] = dy;
       
  2351 					if (heightdiff != 0) params[index++] = heightdiff;
       
  2352 				}
       
  2353 
       
  2354 				GuiShowTooltipsWithArgs(measure_strings_area[index], index, params);
       
  2355 			}
       
  2356 		break;
       
  2357 
       
  2358 		}
       
  2359 		default: NOT_REACHED();
       
  2360 	}
       
  2361 
       
  2362 	_thd.selend.x = x;
       
  2363 	_thd.selend.y = y;
       
  2364 }
       
  2365 
       
  2366 // while dragging
       
  2367 bool VpHandlePlaceSizingDrag(void)
       
  2368 {
       
  2369 	Window *w;
       
  2370 	WindowEvent e;
       
  2371 
       
  2372 	if (_special_mouse_mode != WSM_SIZING) return true;
       
  2373 
       
  2374 	e.we.place.userdata = _thd.userdata;
       
  2375 
       
  2376 	// stop drag mode if the window has been closed
       
  2377 	w = FindWindowById(_thd.window_class,_thd.window_number);
       
  2378 	if (w == NULL) {
       
  2379 		ResetObjectToPlace();
       
  2380 		return false;
       
  2381 	}
       
  2382 
       
  2383 	// while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() )
       
  2384 	if (_left_button_down) {
       
  2385 		e.event = WE_PLACE_DRAG;
       
  2386 		e.we.place.pt = GetTileBelowCursor();
       
  2387 		w->wndproc(w, &e);
       
  2388 		return false;
       
  2389 	}
       
  2390 
       
  2391 	// mouse button released..
       
  2392 	// keep the selected tool, but reset it to the original mode.
       
  2393 	_special_mouse_mode = WSM_NONE;
       
  2394 	if (_thd.next_drawstyle == HT_RECT) {
       
  2395 		_thd.place_mode = VHM_RECT;
       
  2396 	} else if ((e.we.place.userdata & 0xF) == VPM_SIGNALDIRS) { // some might call this a hack... -- Dominik
       
  2397 		_thd.place_mode = VHM_RECT;
       
  2398 	} else if (_thd.next_drawstyle & HT_LINE) {
       
  2399 		_thd.place_mode = VHM_RAIL;
       
  2400 	} else if (_thd.next_drawstyle & HT_RAIL) {
       
  2401 		_thd.place_mode = VHM_RAIL;
       
  2402 	} else {
       
  2403 		_thd.place_mode = VHM_POINT;
       
  2404 	}
       
  2405 	SetTileSelectSize(1, 1);
       
  2406 
       
  2407 	// and call the mouseup event.
       
  2408 	e.event = WE_PLACE_MOUSEUP;
       
  2409 	e.we.place.pt = _thd.selend;
       
  2410 	e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y);
       
  2411 	e.we.place.starttile = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
       
  2412 	w->wndproc(w, &e);
       
  2413 
       
  2414 	return false;
       
  2415 }
       
  2416 
       
  2417 void SetObjectToPlaceWnd(CursorID icon, byte mode, Window *w)
       
  2418 {
       
  2419 	SetObjectToPlace(icon, mode, w->window_class, w->window_number);
       
  2420 }
       
  2421 
       
  2422 #include "table/animcursors.h"
       
  2423 
       
  2424 void SetObjectToPlace(CursorID icon, byte mode, WindowClass window_class, WindowNumber window_num)
       
  2425 {
       
  2426 	Window *w;
       
  2427 
       
  2428 	// undo clicking on button
       
  2429 	if (_thd.place_mode != 0) {
       
  2430 		_thd.place_mode = 0;
       
  2431 		w = FindWindowById(_thd.window_class, _thd.window_number);
       
  2432 		if (w != NULL) CallWindowEventNP(w, WE_ABORT_PLACE_OBJ);
       
  2433 	}
       
  2434 
       
  2435 	SetTileSelectSize(1, 1);
       
  2436 
       
  2437 	_thd.make_square_red = false;
       
  2438 
       
  2439 	if (mode == VHM_DRAG) { // mode 4 is for dragdropping trains in the depot window
       
  2440 		mode = 0;
       
  2441 		_special_mouse_mode = WSM_DRAGDROP;
       
  2442 	} else {
       
  2443 		_special_mouse_mode = WSM_NONE;
       
  2444 	}
       
  2445 
       
  2446 	_thd.place_mode = mode;
       
  2447 	_thd.window_class = window_class;
       
  2448 	_thd.window_number = window_num;
       
  2449 
       
  2450 	if (mode == VHM_SPECIAL) // special tools, like tunnels or docks start with presizing mode
       
  2451 		VpStartPreSizing();
       
  2452 
       
  2453 	if ( (int)icon < 0)
       
  2454 		SetAnimatedMouseCursor(_animcursors[~icon]);
       
  2455 	else
       
  2456 		SetMouseCursor(icon);
       
  2457 }
       
  2458 
       
  2459 void ResetObjectToPlace(void)
       
  2460 {
       
  2461 	SetObjectToPlace(SPR_CURSOR_MOUSE, VHM_NONE, 0, 0);
       
  2462 }