src/viewport.cpp
branchNewGRF_ports
changeset 6871 5a9dc001e1ad
parent 6870 ca3fd1fbe311
child 6872 1c4a4a609f85
equal deleted inserted replaced
6870:ca3fd1fbe311 6871:5a9dc001e1ad
    25 #include "train.h"
    25 #include "train.h"
    26 #include "roadveh.h"
    26 #include "roadveh.h"
    27 #include "vehicle_gui.h"
    27 #include "vehicle_gui.h"
    28 #include "blitter/factory.hpp"
    28 #include "blitter/factory.hpp"
    29 #include "newgrf_fsmports.h"
    29 #include "newgrf_fsmports.h"
       
    30 #include "transparency.h"
    30 
    31 
    31 #define VIEWPORT_DRAW_MEM (65536 * 2)
    32 #define VIEWPORT_DRAW_MEM (65536 * 2)
    32 
    33 
    33 ZoomLevel _saved_scrollpos_zoom;
    34 ZoomLevel _saved_scrollpos_zoom;
    34 
    35 
    35 /* XXX - maximum viewports is maximum windows - 2 (main toolbar + status bar) */
    36 /* XXX - maximum viewports is maximum windows - 2 (main toolbar + status bar) */
    36 static ViewPort _viewports[25 - 2];
    37 static ViewPort _viewports[25 - 2];
    37 static uint32 _active_viewports;    ///< bitmasked variable where each bit signifies if a viewport is in use or not
    38 static uint32 _active_viewports;    ///< bitmasked variable where each bit signifies if a viewport is in use or not
    38 assert_compile(lengthof(_viewports) < sizeof(_active_viewports) * 8);
    39 assert_compile(lengthof(_viewports) < sizeof(_active_viewports) * 8);
    39 
       
    40 static bool _added_tile_sprite;
       
    41 static bool _offset_ground_sprites;
       
    42 
    40 
    43 /* The in-game coordiante system looks like this *
    41 /* The in-game coordiante system looks like this *
    44  *                                               *
    42  *                                               *
    45  *                    ^ Z                        *
    43  *                    ^ Z                        *
    46  *                    |                          *
    44  *                    |                          *
   111 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(StringSpriteToDraw));
   109 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(StringSpriteToDraw));
   112 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(TileSpriteToDraw));
   110 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(TileSpriteToDraw));
   113 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(ChildScreenSpriteToDraw));
   111 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(ChildScreenSpriteToDraw));
   114 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(ParentSpriteToDraw));
   112 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(ParentSpriteToDraw));
   115 
   113 
       
   114 /* Enumeration of multi-part foundations */
       
   115 enum FoundationPart {
       
   116 	FOUNDATION_PART_NONE     = 0xFF,  ///< Neither foundation nor groundsprite drawn yet.
       
   117 	FOUNDATION_PART_NORMAL   = 0,     ///< First part (normal foundation or no foundation)
       
   118 	FOUNDATION_PART_HALFTILE = 1,     ///< Second part (halftile foundation)
       
   119 	FOUNDATION_PART_END
       
   120 };
       
   121 
   116 struct ViewportDrawer {
   122 struct ViewportDrawer {
   117 	DrawPixelInfo dpi;
   123 	DrawPixelInfo dpi;
   118 
   124 
   119 	byte *spritelist_mem;
   125 	byte *spritelist_mem;
   120 	const byte *eof_spritelist_mem;
   126 	const byte *eof_spritelist_mem;
   127 	ParentSpriteToDraw **parent_list;
   133 	ParentSpriteToDraw **parent_list;
   128 	ParentSpriteToDraw * const *eof_parent_list;
   134 	ParentSpriteToDraw * const *eof_parent_list;
   129 
   135 
   130 	byte combine_sprites;
   136 	byte combine_sprites;
   131 
   137 
   132 	int offs_x, offs_y; // used when drawing ground sprites relative
   138 	ParentSpriteToDraw *foundation[FOUNDATION_PART_END];                   ///< Foundation sprites.
       
   139 	FoundationPart foundation_part;                                        ///< Currently active foundation for ground sprite drawing.
       
   140 	ChildScreenSpriteToDraw **last_foundation_child[FOUNDATION_PART_END];  ///< Tail of ChildSprite list of the foundations.
       
   141 	Point foundation_offset[FOUNDATION_PART_END];                          ///< Pixeloffset for ground sprites on the foundations.
   133 };
   142 };
   134 
   143 
   135 static ViewportDrawer *_cur_vd;
   144 static ViewportDrawer *_cur_vd;
   136 
   145 
   137 TileHighlightData _thd;
   146 TileHighlightData _thd;
   153 	_active_viewports = 0;
   162 	_active_viewports = 0;
   154 }
   163 }
   155 
   164 
   156 void DeleteWindowViewport(Window *w)
   165 void DeleteWindowViewport(Window *w)
   157 {
   166 {
   158 	CLRBIT(_active_viewports, w->viewport - _viewports);
   167 	ClrBit(_active_viewports, w->viewport - _viewports);
   159 	w->viewport->width = 0;
   168 	w->viewport->width = 0;
   160 	w->viewport = NULL;
   169 	w->viewport = NULL;
   161 }
   170 }
   162 
   171 
   163 void AssignWindowViewport(Window *w, int x, int y,
   172 void AssignWindowViewport(Window *w, int x, int y,
   169 
   178 
   170 	for (vp = _viewports, bit = 0; ; vp++, bit++) {
   179 	for (vp = _viewports, bit = 0; ; vp++, bit++) {
   171 		assert(vp != endof(_viewports));
   180 		assert(vp != endof(_viewports));
   172 		if (vp->width == 0) break;
   181 		if (vp->width == 0) break;
   173 	}
   182 	}
   174 	SETBIT(_active_viewports, bit);
   183 	SetBit(_active_viewports, bit);
   175 
   184 
   176 	vp->left = x + w->left;
   185 	vp->left = x + w->left;
   177 	vp->top = y + w->top;
   186 	vp->top = y + w->top;
   178 	vp->width = width;
   187 	vp->width = width;
   179 	vp->height = height;
   188 	vp->height = height;
   286 	int left, top, width, height;
   295 	int left, top, width, height;
   287 
   296 
   288 	vp->virtual_left = x;
   297 	vp->virtual_left = x;
   289 	vp->virtual_top = y;
   298 	vp->virtual_top = y;
   290 
   299 
   291 	old_left = UnScaleByZoom(old_left, vp->zoom);
   300 	/* viewport is bound to its left top corner, so it must be rounded down (UnScaleByZoomLower)
   292 	old_top = UnScaleByZoom(old_top, vp->zoom);
   301 	 * else glitch described in FS#1412 will happen (offset by 1 pixel with zoom level > NORMAL)
   293 	x = UnScaleByZoom(x, vp->zoom);
   302 	 */
   294 	y = UnScaleByZoom(y, vp->zoom);
   303 	old_left = UnScaleByZoomLower(old_left, vp->zoom);
       
   304 	old_top = UnScaleByZoomLower(old_top, vp->zoom);
       
   305 	x = UnScaleByZoomLower(x, vp->zoom);
       
   306 	y = UnScaleByZoomLower(y, vp->zoom);
   295 
   307 
   296 	old_left -= x;
   308 	old_left -= x;
   297 	old_top -= y;
   309 	old_top -= y;
   298 
   310 
   299 	if (old_top == 0 && old_left == 0) return;
   311 	if (old_top == 0 && old_left == 0) return;
   331 ViewPort *IsPtInWindowViewport(const Window *w, int x, int y)
   343 ViewPort *IsPtInWindowViewport(const Window *w, int x, int y)
   332 {
   344 {
   333 	ViewPort *vp = w->viewport;
   345 	ViewPort *vp = w->viewport;
   334 
   346 
   335 	if (vp != NULL &&
   347 	if (vp != NULL &&
   336 	    IS_INT_INSIDE(x, vp->left, vp->left + vp->width) &&
   348 	    IsInsideMM(x, vp->left, vp->left + vp->width) &&
   337 			IS_INT_INSIDE(y, vp->top, vp->top + vp->height))
   349 			IsInsideMM(y, vp->top, vp->top + vp->height))
   338 		return vp;
   350 		return vp;
   339 
   351 
   340 	return NULL;
   352 	return NULL;
   341 }
   353 }
   342 
   354 
   359 	b = y+x;
   371 	b = y+x;
   360 
   372 
   361 	/* we need to move variables in to the valid range, as the
   373 	/* we need to move variables in to the valid range, as the
   362 	 * GetTileZoomCenterWindow() function can call here with invalid x and/or y,
   374 	 * GetTileZoomCenterWindow() function can call here with invalid x and/or y,
   363 	 * when the user tries to zoom out along the sides of the map */
   375 	 * when the user tries to zoom out along the sides of the map */
   364 	a = clamp(a, 0, (int)(MapMaxX() * TILE_SIZE) - 1);
   376 	a = Clamp(a, 0, (int)(MapMaxX() * TILE_SIZE) - 1);
   365 	b = clamp(b, 0, (int)(MapMaxY() * TILE_SIZE) - 1);
   377 	b = Clamp(b, 0, (int)(MapMaxY() * TILE_SIZE) - 1);
   366 
   378 
   367 	z = GetSlopeZ(a,     b    ) / 2;
   379 	/* (a, b) is the X/Y-world coordinate that belongs to (x,y) if the landscape would be completely flat on height 0.
   368 	z = GetSlopeZ(a + z, b + z) / 2;
   380 	 * Now find the Z-world coordinate by fix point iteration.
   369 	z = GetSlopeZ(a + z, b + z) / 2;
   381 	 * This is a bit tricky because the tile height is non-continuous at foundations.
   370 	z = GetSlopeZ(a + z, b + z) / 2;
   382 	 * The clicked point should be approached from the back, otherwise there are regions that are not clickable.
   371 	z = GetSlopeZ(a + z, b + z) / 2;
   383 	 * (FOUNDATION_HALFTILE_LOWER on SLOPE_STEEP_S hides north halftile completely)
       
   384 	 * So give it a z-malus of 4 in the first iterations.
       
   385 	 */
       
   386 	z = 0;
       
   387 	for (int i = 0; i < 5; i++) z = GetSlopeZ(a + max(z, 4u) - 4, b + max(z, 4u) - 4) / 2;
       
   388 	for (uint malus = 3; malus > 0; malus--) z = GetSlopeZ(a + max(z, malus) - malus, b + max(z, malus) - malus) / 2;
       
   389 	for (int i = 0; i < 5; i++) z = GetSlopeZ(a + z, b + z) / 2;
   372 
   390 
   373 	pt.x = a + z;
   391 	pt.x = a + z;
   374 	pt.y = b + z;
   392 	pt.y = b + z;
   375 
   393 
   376 	return pt;
   394 	return pt;
   423  * @param vp pointer to the viewport whose zoom-level the buttons represent
   441  * @param vp pointer to the viewport whose zoom-level the buttons represent
   424  * @param widget_zoom_in widget index for window with zoom-in button
   442  * @param widget_zoom_in widget index for window with zoom-in button
   425  * @param widget_zoom_out widget index for window with zoom-out button */
   443  * @param widget_zoom_out widget index for window with zoom-out button */
   426 void HandleZoomMessage(Window *w, const ViewPort *vp, byte widget_zoom_in, byte widget_zoom_out)
   444 void HandleZoomMessage(Window *w, const ViewPort *vp, byte widget_zoom_in, byte widget_zoom_out)
   427 {
   445 {
   428 	SetWindowWidgetDisabledState(w, widget_zoom_in, vp->zoom == ZOOM_LVL_MIN);
   446 	w->SetWidgetDisabledState(widget_zoom_in, vp->zoom == ZOOM_LVL_MIN);
   429 	InvalidateWidget(w, widget_zoom_in);
   447 	InvalidateWidget(w, widget_zoom_in);
   430 
   448 
   431 	SetWindowWidgetDisabledState(w, widget_zoom_out, vp->zoom == ZOOM_LVL_MAX);
   449 	w->SetWidgetDisabledState(widget_zoom_out, vp->zoom == ZOOM_LVL_MAX);
   432 	InvalidateWidget(w, widget_zoom_out);
   450 	InvalidateWidget(w, widget_zoom_out);
   433 }
   451 }
   434 
   452 
   435 /**
   453 /**
   436  * Draws a ground sprite at a specific world-coordinate.
   454  * Draws a ground sprite at a specific world-coordinate.
   468 	*vd->last_tile = ts;
   486 	*vd->last_tile = ts;
   469 	vd->last_tile = &ts->next;
   487 	vd->last_tile = &ts->next;
   470 }
   488 }
   471 
   489 
   472 /**
   490 /**
       
   491  * Adds a child sprite to the active foundation.
       
   492  *
       
   493  * The pixel offset of the sprite relative to the ParentSprite is the sum of the offset passed to OffsetGroundSprite() and extra_offs_?.
       
   494  *
       
   495  * @param image the image to draw.
       
   496  * @param pal the provided palette.
       
   497  * @param sub Only draw a part of the sprite.
       
   498  * @param foundation_part Foundation part.
       
   499  * @param extra_offs_x Pixel X offset for the sprite position.
       
   500  * @param extra_offs_y Pixel Y offset for the sprite position.
       
   501  */
       
   502 static void AddChildSpriteToFoundation(SpriteID image, SpriteID pal, const SubSprite *sub, FoundationPart foundation_part, int extra_offs_x, int extra_offs_y)
       
   503 {
       
   504 	ViewportDrawer *vd = _cur_vd;
       
   505 	assert(IsInsideMM(foundation_part, 0, FOUNDATION_PART_END));
       
   506 	assert(vd->foundation[foundation_part] != NULL);
       
   507 	Point offs = vd->foundation_offset[foundation_part];
       
   508 
       
   509 	/* Change the active ChildSprite list to the one of the foundation */
       
   510 	ChildScreenSpriteToDraw **old_child = vd->last_child;
       
   511 	vd->last_child = vd->last_foundation_child[foundation_part];
       
   512 
       
   513 	AddChildSpriteScreen(image, pal, offs.x + extra_offs_x, offs.y + extra_offs_y, false, sub);
       
   514 
       
   515 	/* Switch back to last ChildSprite list */
       
   516 	vd->last_child = old_child;
       
   517 }
       
   518 
       
   519 /**
   473  * Draws a ground sprite for the current tile.
   520  * Draws a ground sprite for the current tile.
   474  * If the current tile is drawn on top of a foundation the sprite is added as child sprite to the "foundation"-ParentSprite.
   521  * If the current tile is drawn on top of a foundation the sprite is added as child sprite to the "foundation"-ParentSprite.
   475  *
   522  *
   476  * @param image the image to draw.
   523  * @param image the image to draw.
   477  * @param pal the provided palette.
   524  * @param pal the provided palette.
   478  * @param sub Only draw a part of the sprite.
   525  * @param sub Only draw a part of the sprite.
   479  */
   526  */
   480 void DrawGroundSprite(SpriteID image, SpriteID pal, const SubSprite *sub)
   527 void DrawGroundSprite(SpriteID image, SpriteID pal, const SubSprite *sub)
   481 {
   528 {
   482 	if (_offset_ground_sprites) {
   529 	ViewportDrawer *vd = _cur_vd;
   483 		/* offset ground sprite because of foundation? */
   530 	/* Switch to first foundation part, if no foundation was drawn */
   484 		AddChildSpriteScreen(image, pal, _cur_vd->offs_x, _cur_vd->offs_y, false, sub);
   531 	if (vd->foundation_part == FOUNDATION_PART_NONE) vd->foundation_part = FOUNDATION_PART_NORMAL;
       
   532 
       
   533 	if (vd->foundation[vd->foundation_part] != NULL) {
       
   534 		AddChildSpriteToFoundation(image, pal, sub, vd->foundation_part, 0, 0);
   485 	} else {
   535 	} else {
   486 		_added_tile_sprite = true;
       
   487 		DrawGroundSpriteAt(image, pal, _cur_ti->x, _cur_ti->y, _cur_ti->z, sub);
   536 		DrawGroundSpriteAt(image, pal, _cur_ti->x, _cur_ti->y, _cur_ti->z, sub);
   488 	}
   537 	}
   489 }
   538 }
   490 
   539 
   491 
   540 
       
   541 /**
       
   542  * Called when a foundation has been drawn for the current tile.
       
   543  * Successive ground sprites for the current tile will be drawn as child sprites of the "foundation"-ParentSprite, not as TileSprites.
       
   544  *
       
   545  * @param x sprite x-offset (screen coordinates) of ground sprites relative to the "foundation"-ParentSprite.
       
   546  * @param y sprite y-offset (screen coordinates) of ground sprites relative to the "foundation"-ParentSprite.
       
   547  */
   492 void OffsetGroundSprite(int x, int y)
   548 void OffsetGroundSprite(int x, int y)
   493 {
   549 {
   494 	_cur_vd->offs_x = x;
   550 	ViewportDrawer *vd = _cur_vd;
   495 	_cur_vd->offs_y = y;
   551 	/* Switch to next foundation part */
   496 	_offset_ground_sprites = true;
   552 	switch (vd->foundation_part) {
       
   553 		case FOUNDATION_PART_NONE:
       
   554 			vd->foundation_part = FOUNDATION_PART_NORMAL;
       
   555 			break;
       
   556 		case FOUNDATION_PART_NORMAL:
       
   557 			vd->foundation_part = FOUNDATION_PART_HALFTILE;
       
   558 			break;
       
   559 		default: NOT_REACHED();
       
   560 	}
       
   561 
       
   562 	/* vd->last_child == NULL if foundation sprite was clipped by the viewport bounds */
       
   563 	if (vd->last_child != NULL) vd->foundation[vd->foundation_part] = vd->parent_list[-1];
       
   564 
       
   565 	vd->foundation_offset[vd->foundation_part].x = x;
       
   566 	vd->foundation_offset[vd->foundation_part].y = y;
       
   567 	vd->last_foundation_child[vd->foundation_part] = vd->last_child;
   497 }
   568 }
   498 
   569 
   499 /**
   570 /**
   500  * Adds a child sprite to a parent sprite.
   571  * Adds a child sprite to a parent sprite.
   501  * In contrast to "AddChildSpriteScreen()" the sprite position is in world coordinates
   572  * In contrast to "AddChildSpriteScreen()" the sprite position is in world coordinates
   555 
   626 
   556 	assert((image & SPRITE_MASK) < MAX_SPRITES);
   627 	assert((image & SPRITE_MASK) < MAX_SPRITES);
   557 
   628 
   558 	/* make the sprites transparent with the right palette */
   629 	/* make the sprites transparent with the right palette */
   559 	if (transparent) {
   630 	if (transparent) {
   560 		SETBIT(image, PALETTE_MODIFIER_TRANSPARENT);
   631 		SetBit(image, PALETTE_MODIFIER_TRANSPARENT);
   561 		pal = PALETTE_TO_TRANSPARENT;
   632 		pal = PALETTE_TO_TRANSPARENT;
   562 	}
   633 	}
   563 
   634 
   564 	if (vd->combine_sprites == 2) {
   635 	if (vd->combine_sprites == 2) {
   565 		AddCombinedSprite(image, pal, x, y, z, sub);
   636 		AddCombinedSprite(image, pal, x, y, z, sub);
   667 
   738 
   668 	assert((image & SPRITE_MASK) < MAX_SPRITES);
   739 	assert((image & SPRITE_MASK) < MAX_SPRITES);
   669 
   740 
   670 	/* make the sprites transparent with the right palette */
   741 	/* make the sprites transparent with the right palette */
   671 	if (transparent) {
   742 	if (transparent) {
   672 		SETBIT(image, PALETTE_MODIFIER_TRANSPARENT);
   743 		SetBit(image, PALETTE_MODIFIER_TRANSPARENT);
   673 		pal = PALETTE_TO_TRANSPARENT;
   744 		pal = PALETTE_TO_TRANSPARENT;
   674 	}
   745 	}
   675 
   746 
   676 	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
   747 	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
   677 		DEBUG(sprite, 0, "Out of sprite memory");
   748 		DEBUG(sprite, 0, "Out of sprite memory");
   682 	/* If the ParentSprite was clipped by the viewport bounds, do not draw the ChildSprites either */
   753 	/* If the ParentSprite was clipped by the viewport bounds, do not draw the ChildSprites either */
   683 	if (vd->last_child == NULL) return;
   754 	if (vd->last_child == NULL) return;
   684 
   755 
   685 	vd->spritelist_mem += sizeof(ChildScreenSpriteToDraw);
   756 	vd->spritelist_mem += sizeof(ChildScreenSpriteToDraw);
   686 
   757 
       
   758 	/* Append the sprite to the active ChildSprite list.
       
   759 	 * If the active ParentSprite is a foundation, update last_foundation_child as well. */
   687 	*vd->last_child = cs;
   760 	*vd->last_child = cs;
       
   761 	if (vd->last_foundation_child[0] == vd->last_child) vd->last_foundation_child[0] = &cs->next;
       
   762 	if (vd->last_foundation_child[1] == vd->last_child) vd->last_foundation_child[1] = &cs->next;
   688 	vd->last_child = &cs->next;
   763 	vd->last_child = &cs->next;
   689 
   764 
   690 	cs->image = image;
   765 	cs->image = image;
   691 	cs->pal = pal;
   766 	cs->pal = pal;
   692 	cs->sub = sub;
   767 	cs->sub = sub;
   722 
   797 
   723 	return ss;
   798 	return ss;
   724 }
   799 }
   725 
   800 
   726 
   801 
   727 static void DrawSelectionSprite(SpriteID image, SpriteID pal, const TileInfo *ti)
   802 /**
   728 {
   803  * Draws sprites between ground sprite and everything above.
   729 	if (_added_tile_sprite && !(_thd.drawstyle & HT_LINE)) { // draw on real ground
   804  *
   730 		DrawGroundSpriteAt(image, pal, ti->x, ti->y, ti->z + 7);
   805  * The sprite is either drawn as TileSprite or as ChildSprite of the active foundation.
   731 	} else { // draw on top of foundation
   806  *
   732 		AddSortableSpriteToDraw(image, pal, ti->x, ti->y, 0x10, 0x10, 1, ti->z + 7);
   807  * @param image the image to draw.
       
   808  * @param pal the provided palette.
       
   809  * @param ti TileInfo Tile that is being drawn
       
   810  * @param z_offset Z offset relative to the groundsprite. Only used for the sprite position, not for sprite sorting.
       
   811  * @param foundation_part Foundation part the sprite belongs to.
       
   812  */
       
   813 static void DrawSelectionSprite(SpriteID image, SpriteID pal, const TileInfo *ti, int z_offset, FoundationPart foundation_part)
       
   814 {
       
   815 	/* FIXME: This is not totally valid for some autorail highlights, that extent over the edges of the tile. */
       
   816 	if (_cur_vd->foundation[foundation_part] == NULL) {
       
   817 		/* draw on real ground */
       
   818 		DrawGroundSpriteAt(image, pal, ti->x, ti->y, ti->z + z_offset);
       
   819 	} else {
       
   820 		/* draw on top of foundation */
       
   821 		AddChildSpriteToFoundation(image, pal, NULL, foundation_part, 0, -z_offset);
   733 	}
   822 	}
   734 }
   823 }
   735 
   824 
   736 /**
   825 /**
   737  * Draws a selection rectangle on a tile.
   826  * Draws a selection rectangle on a tile.
   739  * @param ti TileInfo Tile that is being drawn
   828  * @param ti TileInfo Tile that is being drawn
   740  * @param pal Palette to apply.
   829  * @param pal Palette to apply.
   741  */
   830  */
   742 static void DrawTileSelectionRect(const TileInfo *ti, SpriteID pal)
   831 static void DrawTileSelectionRect(const TileInfo *ti, SpriteID pal)
   743 {
   832 {
   744 	DrawSelectionSprite(SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh], pal, ti);
   833 	SpriteID sel;
       
   834 	if (IsHalftileSlope(ti->tileh)) {
       
   835 		Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
       
   836 		SpriteID sel2 = SPR_HALFTILE_SELECTION_FLAT + halftile_corner;
       
   837 		DrawSelectionSprite(sel2, pal, ti, 7 + TILE_HEIGHT, FOUNDATION_PART_HALFTILE);
       
   838 
       
   839 		Corner opposite_corner = OppositeCorner(halftile_corner);
       
   840 		if (IsSteepSlope(ti->tileh)) {
       
   841 			sel = SPR_HALFTILE_SELECTION_DOWN;
       
   842 		} else {
       
   843 			sel = ((ti->tileh & SlopeWithOneCornerRaised(opposite_corner)) != 0 ? SPR_HALFTILE_SELECTION_UP : SPR_HALFTILE_SELECTION_FLAT);
       
   844 		}
       
   845 		sel += opposite_corner;
       
   846 	} else {
       
   847 		sel = SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh];
       
   848 	}
       
   849 	DrawSelectionSprite(sel, pal, ti, 7, FOUNDATION_PART_NORMAL);
   745 }
   850 }
   746 
   851 
   747 static bool IsPartOfAutoLine(int px, int py)
   852 static bool IsPartOfAutoLine(int px, int py)
   748 {
   853 {
   749 	px -= _thd.selstart.x;
   854 	px -= _thd.selstart.x;
   786 {
   891 {
   787 	SpriteID image;
   892 	SpriteID image;
   788 	SpriteID pal;
   893 	SpriteID pal;
   789 	int offset;
   894 	int offset;
   790 
   895 
   791 	offset = _AutorailTilehSprite[ti->tileh][autorail_type];
   896 	FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
       
   897 	Slope autorail_tileh = (Slope)(ti->tileh & ~SLOPE_HALFTILE_MASK);
       
   898 	if (IsHalftileSlope(ti->tileh)) {
       
   899 		static const uint _lower_rail[4] = { 5U, 2U, 4U, 3U };
       
   900 		Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
       
   901 		if (autorail_type != _lower_rail[halftile_corner]) {
       
   902 			foundation_part = FOUNDATION_PART_HALFTILE;
       
   903 			/* Here we draw the highlights of the "three-corners-raised"-slope. That looks ok to me. */
       
   904 			autorail_tileh = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
       
   905 		}
       
   906 	}
       
   907 
       
   908 	offset = _AutorailTilehSprite[autorail_tileh][autorail_type];
   792 	if (offset >= 0) {
   909 	if (offset >= 0) {
   793 		image = SPR_AUTORAIL_BASE + offset;
   910 		image = SPR_AUTORAIL_BASE + offset;
   794 		pal = PAL_NONE;
   911 		pal = PAL_NONE;
   795 	} else {
   912 	} else {
   796 		image = SPR_AUTORAIL_BASE - offset;
   913 		image = SPR_AUTORAIL_BASE - offset;
   797 		pal = PALETTE_SEL_TILE_RED;
   914 		pal = PALETTE_SEL_TILE_RED;
   798 	}
   915 	}
   799 
   916 
   800 	DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti);
   917 	DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7, foundation_part);
   801 }
   918 }
   802 
   919 
   803 /**
   920 /**
   804  * Checks if the specified tile is selected and if so draws selection using correct selectionstyle.
   921  * Checks if the specified tile is selected and if so draws selection using correct selectionstyle.
   805  * @param *ti TileInfo Tile that is being drawn
   922  * @param *ti TileInfo Tile that is being drawn
   814 
   931 
   815 	/* no selection active? */
   932 	/* no selection active? */
   816 	if (_thd.drawstyle == 0) return;
   933 	if (_thd.drawstyle == 0) return;
   817 
   934 
   818 	/* Inside the inner area? */
   935 	/* Inside the inner area? */
   819 	if (IS_INSIDE_1D(ti->x, _thd.pos.x, _thd.size.x) &&
   936 	if (IsInsideBS(ti->x, _thd.pos.x, _thd.size.x) &&
   820 			IS_INSIDE_1D(ti->y, _thd.pos.y, _thd.size.y)) {
   937 			IsInsideBS(ti->y, _thd.pos.y, _thd.size.y)) {
   821 		if (_thd.drawstyle & HT_RECT) {
   938 		if (_thd.drawstyle & HT_RECT) {
   822 			if (_thd.FSMportLayout != NULL) {
   939 			if (_thd.FSMportLayout != NULL) {
   823 				byte *b = *_thd.FSMportLayout;
   940 				byte *b = *_thd.FSMportLayout;
   824 				byte *m;
   941 				byte *m;
   825 				if (_thd.FSMportMask != NULL) m = *_thd.FSMportMask;
   942 				if (_thd.FSMportMask != NULL) m = *_thd.FSMportMask;
   844 			if (_thd.FSMportLayout == NULL) {
   961 			if (_thd.FSMportLayout == NULL) {
   845 				DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE);
   962 				DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE);
   846 			}
   963 			}
   847 		} else if (_thd.drawstyle & HT_POINT) {
   964 		} else if (_thd.drawstyle & HT_POINT) {
   848 			/* Figure out the Z coordinate for the single dot. */
   965 			/* Figure out the Z coordinate for the single dot. */
   849 			byte z = ti->z;
   966 			byte z = 0;
       
   967 			FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
   850 			if (ti->tileh & SLOPE_N) {
   968 			if (ti->tileh & SLOPE_N) {
   851 				z += TILE_HEIGHT;
   969 				z += TILE_HEIGHT;
   852 				if (ti->tileh == SLOPE_STEEP_N) z += TILE_HEIGHT;
   970 				if ((ti->tileh & ~SLOPE_HALFTILE_MASK) == SLOPE_STEEP_N) z += TILE_HEIGHT;
   853 			}
   971 			}
   854 			DrawGroundSpriteAt(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti->x, ti->y, z);
   972 			if (IsHalftileSlope(ti->tileh)) {
       
   973 				Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
       
   974 				if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT;
       
   975 				if (halftile_corner != CORNER_S) {
       
   976 					foundation_part = FOUNDATION_PART_HALFTILE;
       
   977 					if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT;
       
   978 				}
       
   979 			}
       
   980 			DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti, z, foundation_part);
   855 		} else if (_thd.drawstyle & HT_RAIL /*&& _thd.place_mode == VHM_RAIL*/) {
   981 		} else if (_thd.drawstyle & HT_RAIL /*&& _thd.place_mode == VHM_RAIL*/) {
   856 			/* autorail highlight piece under cursor */
   982 			/* autorail highlight piece under cursor */
   857 			uint type = _thd.drawstyle & 0xF;
   983 			uint type = _thd.drawstyle & 0xF;
   858 			assert(type <= 5);
   984 			assert(type <= 5);
   859 			DrawAutorailSelection(ti, _AutorailType[type][0]);
   985 			DrawAutorailSelection(ti, _AutorailType[type][0]);
   860 
       
   861 		} else if (IsPartOfAutoLine(ti->x, ti->y)) {
   986 		} else if (IsPartOfAutoLine(ti->x, ti->y)) {
   862 			/* autorail highlighting long line */
   987 			/* autorail highlighting long line */
   863 			int dir = _thd.drawstyle & ~0xF0;
   988 			int dir = _thd.drawstyle & ~0xF0;
   864 			uint side;
   989 			uint side;
   865 
   990 
   866 			if (dir < 2) {
   991 			if (dir < 2) {
   867 				side = 0;
   992 				side = 0;
   868 			} else {
   993 			} else {
   869 				TileIndex start = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
   994 				TileIndex start = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
   870 				side = delta(delta(TileX(start), TileX(ti->tile)), delta(TileY(start), TileY(ti->tile)));
   995 				side = Delta(Delta(TileX(start), TileX(ti->tile)), Delta(TileY(start), TileY(ti->tile)));
   871 			}
   996 			}
   872 
   997 
   873 			DrawAutorailSelection(ti, _AutorailType[dir][side]);
   998 			DrawAutorailSelection(ti, _AutorailType[dir][side]);
   874 		}
   999 		}
   875 		return;
  1000 		return;
   876 	}
  1001 	}
   877 
  1002 
   878 	/* Check if it's inside the outer area? */
  1003 	/* Check if it's inside the outer area? */
   879 	if (_thd.outersize.x &&
  1004 	if (_thd.outersize.x &&
   880 			_thd.size.x < _thd.size.x + _thd.outersize.x &&
  1005 			_thd.size.x < _thd.size.x + _thd.outersize.x &&
   881 			IS_INSIDE_1D(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
  1006 			IsInsideBS(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
   882 			IS_INSIDE_1D(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
  1007 			IsInsideBS(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
   883 		/* Draw a blue rect. */
  1008 		/* Draw a blue rect. */
   884 		DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE);
  1009 		DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE);
   885 		return;
  1010 		return;
   886 	}
  1011 	}
   887 }
  1012 }
   936 			}
  1061 			}
   937 
  1062 
   938 			y_cur += 0x10;
  1063 			y_cur += 0x10;
   939 			x_cur -= 0x10;
  1064 			x_cur -= 0x10;
   940 
  1065 
   941 			_added_tile_sprite = false;
  1066 			vd->foundation_part = FOUNDATION_PART_NONE;
   942 			_offset_ground_sprites = false;
  1067 			vd->foundation[0] = NULL;
       
  1068 			vd->foundation[1] = NULL;
       
  1069 			vd->last_foundation_child[0] = NULL;
       
  1070 			vd->last_foundation_child[1] = NULL;
   943 
  1071 
   944 			_tile_type_procs[tt]->draw_tile_proc(&ti);
  1072 			_tile_type_procs[tt]->draw_tile_proc(&ti);
   945 			DrawTileSelection(&ti);
  1073 			DrawTileSelection(&ti);
   946 		} while (--width_cur);
  1074 		} while (--width_cur);
   947 
  1075 
   957 static void ViewportAddTownNames(DrawPixelInfo *dpi)
  1085 static void ViewportAddTownNames(DrawPixelInfo *dpi)
   958 {
  1086 {
   959 	Town *t;
  1087 	Town *t;
   960 	int left, top, right, bottom;
  1088 	int left, top, right, bottom;
   961 
  1089 
   962 	if (!HASBIT(_display_opt, DO_SHOW_TOWN_NAMES) || _game_mode == GM_MENU)
  1090 	if (!HasBit(_display_opt, DO_SHOW_TOWN_NAMES) || _game_mode == GM_MENU)
   963 		return;
  1091 		return;
   964 
  1092 
   965 	left = dpi->left;
  1093 	left = dpi->left;
   966 	top = dpi->top;
  1094 	top = dpi->top;
   967 	right = left + dpi->width;
  1095 	right = left + dpi->width;
  1033 static void ViewportAddStationNames(DrawPixelInfo *dpi)
  1161 static void ViewportAddStationNames(DrawPixelInfo *dpi)
  1034 {
  1162 {
  1035 	int left, top, right, bottom;
  1163 	int left, top, right, bottom;
  1036 	const Station *st;
  1164 	const Station *st;
  1037 
  1165 
  1038 	if (!HASBIT(_display_opt, DO_SHOW_STATION_NAMES) || _game_mode == GM_MENU)
  1166 	if (!HasBit(_display_opt, DO_SHOW_STATION_NAMES) || _game_mode == GM_MENU)
  1039 		return;
  1167 		return;
  1040 
  1168 
  1041 	left = dpi->left;
  1169 	left = dpi->left;
  1042 	top = dpi->top;
  1170 	top = dpi->top;
  1043 	right = left + dpi->width;
  1171 	right = left + dpi->width;
  1103 static void ViewportAddSigns(DrawPixelInfo *dpi)
  1231 static void ViewportAddSigns(DrawPixelInfo *dpi)
  1104 {
  1232 {
  1105 	const Sign *si;
  1233 	const Sign *si;
  1106 	int left, top, right, bottom;
  1234 	int left, top, right, bottom;
  1107 
  1235 
  1108 	if (!HASBIT(_display_opt, DO_SHOW_SIGNS))
  1236 	if (!HasBit(_display_opt, DO_SHOW_SIGNS))
  1109 		return;
  1237 		return;
  1110 
  1238 
  1111 	left = dpi->left;
  1239 	left = dpi->left;
  1112 	top = dpi->top;
  1240 	top = dpi->top;
  1113 	right = left + dpi->width;
  1241 	right = left + dpi->width;
  1146 			FOR_ALL_SIGNS(si) {
  1274 			FOR_ALL_SIGNS(si) {
  1147 				if (bottom > si->sign.top &&
  1275 				if (bottom > si->sign.top &&
  1148 						top    < si->sign.top + ScaleByZoom(12, dpi->zoom) &&
  1276 						top    < si->sign.top + ScaleByZoom(12, dpi->zoom) &&
  1149 						right  > si->sign.left &&
  1277 						right  > si->sign.left &&
  1150 						left   < si->sign.left + ScaleByZoom(si->sign.width_2, dpi->zoom)) {
  1278 						left   < si->sign.left + ScaleByZoom(si->sign.width_2, dpi->zoom)) {
  1151 					AddSign(si, STR_2002, si->sign.width_2 | 0x8000);
  1279 					AddSign(si, IsTransparencySet(TO_SIGNS) ? STR_2002_WHITE : STR_2002, si->sign.width_2 | 0x8000);
  1152 				}
  1280 				}
  1153 			}
  1281 			}
  1154 			break;
  1282 			break;
  1155 
  1283 
  1156 		default: NOT_REACHED();
  1284 		default: NOT_REACHED();
  1173 static void ViewportAddWaypoints(DrawPixelInfo *dpi)
  1301 static void ViewportAddWaypoints(DrawPixelInfo *dpi)
  1174 {
  1302 {
  1175 	const Waypoint *wp;
  1303 	const Waypoint *wp;
  1176 	int left, top, right, bottom;
  1304 	int left, top, right, bottom;
  1177 
  1305 
  1178 	if (!HASBIT(_display_opt, DO_WAYPOINTS))
  1306 	if (!HasBit(_display_opt, DO_WAYPOINTS))
  1179 		return;
  1307 		return;
  1180 
  1308 
  1181 	left = dpi->left;
  1309 	left = dpi->left;
  1182 	top = dpi->top;
  1310 	top = dpi->top;
  1183 	right = left + dpi->width;
  1311 	right = left + dpi->width;
  1382 				w -= 3;
  1510 				w -= 3;
  1383 			}
  1511 			}
  1384 
  1512 
  1385 		/* Draw the rectangle if 'tranparent station signs' is off,
  1513 		/* Draw the rectangle if 'tranparent station signs' is off,
  1386 		 * or if we are drawing a general text sign (STR_2806) */
  1514 		 * or if we are drawing a general text sign (STR_2806) */
  1387 			if (!HASBIT(_transparent_opt, TO_SIGNS) || ss->string == STR_2806) {
  1515 			if (!IsTransparencySet(TO_SIGNS) || ss->string == STR_2806) {
  1388 				DrawFrameRect(
  1516 				DrawFrameRect(
  1389 					x, y, x + w, bottom, ss->color,
  1517 					x, y, x + w, bottom, ss->color,
  1390 					HASBIT(_transparent_opt, TO_SIGNS) ? FR_TRANSPARENT : FR_NONE
  1518 					IsTransparencySet(TO_SIGNS) ? FR_TRANSPARENT : FR_NONE
  1391 				);
  1519 				);
  1392 			}
  1520 			}
  1393 		}
  1521 		}
  1394 
  1522 
  1395 		SetDParam(0, ss->params[0]);
  1523 		SetDParam(0, ss->params[0]);
  1396 		SetDParam(1, ss->params[1]);
  1524 		SetDParam(1, ss->params[1]);
  1397 		/* if we didn't draw a rectangle, or if transparant building is on,
  1525 		/* if we didn't draw a rectangle, or if transparant building is on,
  1398 		 * draw the text in the color the rectangle would have */
  1526 		 * draw the text in the color the rectangle would have */
  1399 		if (HASBIT(_transparent_opt, TO_SIGNS) && ss->string != STR_2806 && ss->width != 0) {
  1527 		if (IsTransparencySet(TO_SIGNS) && ss->string != STR_2806 && ss->width != 0) {
  1400 			/* Real colors need the IS_PALETTE_COLOR flag
  1528 			/* Real colors need the IS_PALETTE_COLOR flag
  1401 			 * otherwise colors from _string_colormap are assumed. */
  1529 			 * otherwise colors from _string_colormap are assumed. */
  1402 			colour = _colour_gradient[ss->color][6] | IS_PALETTE_COLOR;
  1530 			colour = _colour_gradient[ss->color][6] | IS_PALETTE_COLOR;
  1403 		} else {
  1531 		} else {
  1404 			colour = 16;
  1532 			colour = TC_BLACK;
  1405 		}
  1533 		}
  1406 		DrawString(
  1534 		DrawString(
  1407 			UnScaleByZoom(ss->x, zoom), UnScaleByZoom(ss->y, zoom) - (ss->width & 0x8000 ? 2 : 0),
  1535 			UnScaleByZoom(ss->x, zoom), UnScaleByZoom(ss->y, zoom) - (ss->width & 0x8000 ? 2 : 0),
  1408 			ss->string, colour
  1536 			ss->string, colour
  1409 		);
  1537 		);
  1544 	 * Calculation is scaled by 4 to avoid rounding errors */
  1672 	 * Calculation is scaled by 4 to avoid rounding errors */
  1545 	int vx = -x + y * 2;
  1673 	int vx = -x + y * 2;
  1546 	int vy =  x + y * 2;
  1674 	int vy =  x + y * 2;
  1547 
  1675 
  1548 	/* clamp to size of map */
  1676 	/* clamp to size of map */
  1549 	vx = clamp(vx, 0, MapMaxX() * TILE_SIZE * 4);
  1677 	vx = Clamp(vx, 0, MapMaxX() * TILE_SIZE * 4);
  1550 	vy = clamp(vy, 0, MapMaxY() * TILE_SIZE * 4);
  1678 	vy = Clamp(vy, 0, MapMaxY() * TILE_SIZE * 4);
  1551 
  1679 
  1552 	/* Convert map coordinates to viewport coordinates */
  1680 	/* Convert map coordinates to viewport coordinates */
  1553 	x = (-vx + vy) / 2;
  1681 	x = (-vx + vy) / 2;
  1554 	y = ( vx + vy) / 4;
  1682 	y = ( vx + vy) / 4;
  1555 
  1683 
  1576 
  1704 
  1577 		if (delta_x != 0 || delta_y != 0) {
  1705 		if (delta_x != 0 || delta_y != 0) {
  1578 			if (_patches.smooth_scroll) {
  1706 			if (_patches.smooth_scroll) {
  1579 				int max_scroll = ScaleByMapSize1D(512);
  1707 				int max_scroll = ScaleByMapSize1D(512);
  1580 				/* Not at our desired positon yet... */
  1708 				/* Not at our desired positon yet... */
  1581 				WP(w, vp_d).scrollpos_x += clamp(delta_x / 4, -max_scroll, max_scroll);
  1709 				WP(w, vp_d).scrollpos_x += Clamp(delta_x / 4, -max_scroll, max_scroll);
  1582 				WP(w, vp_d).scrollpos_y += clamp(delta_y / 4, -max_scroll, max_scroll);
  1710 				WP(w, vp_d).scrollpos_y += Clamp(delta_y / 4, -max_scroll, max_scroll);
  1583 			} else {
  1711 			} else {
  1584 				WP(w, vp_d).scrollpos_x = WP(w, vp_d).dest_scrollpos_x;
  1712 				WP(w, vp_d).scrollpos_x = WP(w, vp_d).dest_scrollpos_x;
  1585 				WP(w, vp_d).scrollpos_y = WP(w, vp_d).dest_scrollpos_y;
  1713 				WP(w, vp_d).scrollpos_y = WP(w, vp_d).dest_scrollpos_y;
  1586 			}
  1714 			}
  1587 		}
  1715 		}
  1649 void MarkTileDirty(int x, int y)
  1777 void MarkTileDirty(int x, int y)
  1650 {
  1778 {
  1651 	uint z = 0;
  1779 	uint z = 0;
  1652 	Point pt;
  1780 	Point pt;
  1653 
  1781 
  1654 	if (IS_INT_INSIDE(x, 0, MapSizeX() * TILE_SIZE) &&
  1782 	if (IsInsideMM(x, 0, MapSizeX() * TILE_SIZE) &&
  1655 			IS_INT_INSIDE(y, 0, MapSizeY() * TILE_SIZE))
  1783 			IsInsideMM(y, 0, MapSizeY() * TILE_SIZE))
  1656 		z = GetTileZ(TileVirtXY(x, y));
  1784 		z = GetTileZ(TileVirtXY(x, y));
  1657 	pt = RemapCoords(x, y, z);
  1785 	pt = RemapCoords(x, y, z);
  1658 
  1786 
  1659 	MarkAllViewportsDirty(
  1787 	MarkAllViewportsDirty(
  1660 		pt.x - 31,
  1788 		pt.x - 31,
  1713 
  1841 
  1714 static bool CheckClickOnTown(const ViewPort *vp, int x, int y)
  1842 static bool CheckClickOnTown(const ViewPort *vp, int x, int y)
  1715 {
  1843 {
  1716 	const Town *t;
  1844 	const Town *t;
  1717 
  1845 
  1718 	if (!HASBIT(_display_opt, DO_SHOW_TOWN_NAMES)) return false;
  1846 	if (!HasBit(_display_opt, DO_SHOW_TOWN_NAMES)) return false;
  1719 
  1847 
  1720 	switch (vp->zoom) {
  1848 	switch (vp->zoom) {
  1721 		case ZOOM_LVL_NORMAL:
  1849 		case ZOOM_LVL_NORMAL:
  1722 			x = x - vp->left + vp->virtual_left;
  1850 			x = x - vp->left + vp->virtual_left;
  1723 			y = y - vp->top  + vp->virtual_top;
  1851 			y = y - vp->top  + vp->virtual_top;
  1771 
  1899 
  1772 static bool CheckClickOnStation(const ViewPort *vp, int x, int y)
  1900 static bool CheckClickOnStation(const ViewPort *vp, int x, int y)
  1773 {
  1901 {
  1774 	const Station *st;
  1902 	const Station *st;
  1775 
  1903 
  1776 	if (!HASBIT(_display_opt, DO_SHOW_STATION_NAMES)) return false;
  1904 	if (!HasBit(_display_opt, DO_SHOW_STATION_NAMES)) return false;
  1777 
  1905 
  1778 	switch (vp->zoom) {
  1906 	switch (vp->zoom) {
  1779 		case ZOOM_LVL_NORMAL:
  1907 		case ZOOM_LVL_NORMAL:
  1780 			x = x - vp->left + vp->virtual_left;
  1908 			x = x - vp->left + vp->virtual_left;
  1781 			y = y - vp->top  + vp->virtual_top;
  1909 			y = y - vp->top  + vp->virtual_top;
  1829 
  1957 
  1830 static bool CheckClickOnSign(const ViewPort *vp, int x, int y)
  1958 static bool CheckClickOnSign(const ViewPort *vp, int x, int y)
  1831 {
  1959 {
  1832 	const Sign *si;
  1960 	const Sign *si;
  1833 
  1961 
  1834 	if (!HASBIT(_display_opt, DO_SHOW_SIGNS) || _current_player == PLAYER_SPECTATOR) return false;
  1962 	if (!HasBit(_display_opt, DO_SHOW_SIGNS) || _current_player == PLAYER_SPECTATOR) return false;
  1835 
  1963 
  1836 	switch (vp->zoom) {
  1964 	switch (vp->zoom) {
  1837 		case ZOOM_LVL_NORMAL:
  1965 		case ZOOM_LVL_NORMAL:
  1838 			x = x - vp->left + vp->virtual_left;
  1966 			x = x - vp->left + vp->virtual_left;
  1839 			y = y - vp->top  + vp->virtual_top;
  1967 			y = y - vp->top  + vp->virtual_top;
  1887 
  2015 
  1888 static bool CheckClickOnWaypoint(const ViewPort *vp, int x, int y)
  2016 static bool CheckClickOnWaypoint(const ViewPort *vp, int x, int y)
  1889 {
  2017 {
  1890 	const Waypoint *wp;
  2018 	const Waypoint *wp;
  1891 
  2019 
  1892 	if (!HASBIT(_display_opt, DO_WAYPOINTS)) return false;
  2020 	if (!HasBit(_display_opt, DO_WAYPOINTS)) return false;
  1893 
  2021 
  1894 	switch (vp->zoom) {
  2022 	switch (vp->zoom) {
  1895 		case ZOOM_LVL_NORMAL:
  2023 		case ZOOM_LVL_NORMAL:
  1896 			x = x - vp->left + vp->virtual_left;
  2024 			x = x - vp->left + vp->virtual_left;
  1897 			y = y - vp->top  + vp->virtual_top;
  2025 			y = y - vp->top  + vp->virtual_top;
  2136 
  2264 
  2137 	if (_thd.place_mode == VHM_SPECIAL) {
  2265 	if (_thd.place_mode == VHM_SPECIAL) {
  2138 		x1 = _thd.selend.x;
  2266 		x1 = _thd.selend.x;
  2139 		y1 = _thd.selend.y;
  2267 		y1 = _thd.selend.y;
  2140 		if (x1 != -1) {
  2268 		if (x1 != -1) {
  2141 			int x2 = _thd.selstart.x;
  2269 			int x2 = _thd.selstart.x & ~0xF;
  2142 			int y2 = _thd.selstart.y;
  2270 			int y2 = _thd.selstart.y & ~0xF;
  2143 			x1 &= ~0xF;
  2271 			x1 &= ~0xF;
  2144 			y1 &= ~0xF;
  2272 			y1 &= ~0xF;
  2145 
  2273 
  2146 			if (x1 >= x2) Swap(x1, x2);
  2274 			if (x1 >= x2) Swap(x1, x2);
  2147 			if (y1 >= y2) Swap(y1, y2);
  2275 			if (y1 >= y2) Swap(y1, y2);
  2165 					x1 += 8;
  2293 					x1 += 8;
  2166 					y1 += 8;
  2294 					y1 += 8;
  2167 					break;
  2295 					break;
  2168 				case VHM_RAIL:
  2296 				case VHM_RAIL:
  2169 					_thd.new_drawstyle = GetAutorailHT(pt.x, pt.y); // draw one highlighted tile
  2297 					_thd.new_drawstyle = GetAutorailHT(pt.x, pt.y); // draw one highlighted tile
       
  2298 					break;
       
  2299 				default:
       
  2300 					NOT_REACHED();
       
  2301 					break;
  2170 			}
  2302 			}
  2171 			_thd.new_pos.x = x1 & ~0xF;
  2303 			_thd.new_pos.x = x1 & ~0xF;
  2172 			_thd.new_pos.y = y1 & ~0xF;
  2304 			_thd.new_pos.y = y1 & ~0xF;
  2173 		}
  2305 		}
  2174 	}
  2306 	}
  2192 		if (_thd.new_drawstyle) SetSelectionTilesDirty();
  2324 		if (_thd.new_drawstyle) SetSelectionTilesDirty();
  2193 	}
  2325 	}
  2194 }
  2326 }
  2195 
  2327 
  2196 /** highlighting tiles while only going over them with the mouse */
  2328 /** highlighting tiles while only going over them with the mouse */
  2197 void VpStartPlaceSizing(TileIndex tile, byte method, byte process)
  2329 void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, byte process)
  2198 {
  2330 {
  2199 	_thd.select_method = method;
  2331 	_thd.select_method = method;
  2200 	_thd.select_proc   = process;
  2332 	_thd.select_proc   = process;
  2201 	_thd.selend.x = TileX(tile) * TILE_SIZE;
  2333 	_thd.selend.x = TileX(tile) * TILE_SIZE;
  2202 	_thd.selstart.x = TileX(tile) * TILE_SIZE;
  2334 	_thd.selstart.x = TileX(tile) * TILE_SIZE;
  2203 	_thd.selend.y = TileY(tile) * TILE_SIZE;
  2335 	_thd.selend.y = TileY(tile) * TILE_SIZE;
  2204 	_thd.selstart.y = TileY(tile) * TILE_SIZE;
  2336 	_thd.selstart.y = TileY(tile) * TILE_SIZE;
       
  2337 
       
  2338 	/* Needed so several things (road, autoroad, bridges, ...) are placed correctly.
       
  2339 	 * In effect, placement starts from the centre of a tile
       
  2340 	 */
       
  2341 	if (method == VPM_X_OR_Y || method == VPM_FIX_X || method == VPM_FIX_Y) {
       
  2342 		_thd.selend.x += TILE_SIZE / 2;
       
  2343 		_thd.selend.y += TILE_SIZE / 2;
       
  2344 		_thd.selstart.x += TILE_SIZE / 2;
       
  2345 		_thd.selstart.y += TILE_SIZE / 2;
       
  2346 	}
       
  2347 
  2205 	if (_thd.place_mode == VHM_RECT) {
  2348 	if (_thd.place_mode == VHM_RECT) {
  2206 		_thd.place_mode = VHM_SPECIAL;
  2349 		_thd.place_mode = VHM_SPECIAL;
  2207 		_thd.next_drawstyle = HT_RECT;
  2350 		_thd.next_drawstyle = HT_RECT;
  2208 	} else if (_thd.place_mode == VHM_RAIL) { // autorail one piece
  2351 	} else if (_thd.place_mode == VHM_RAIL) { // autorail one piece
  2209 		_thd.place_mode = VHM_SPECIAL;
  2352 		_thd.place_mode = VHM_SPECIAL;
  2402 	HighLightStyle b;
  2545 	HighLightStyle b;
  2403 	uint w, h;
  2546 	uint w, h;
  2404 
  2547 
  2405 	int dx = thd->selstart.x - (thd->selend.x & ~0xF);
  2548 	int dx = thd->selstart.x - (thd->selend.x & ~0xF);
  2406 	int dy = thd->selstart.y - (thd->selend.y & ~0xF);
  2549 	int dy = thd->selstart.y - (thd->selend.y & ~0xF);
  2407 	w = myabs(dx) + 16;
  2550 	w = abs(dx) + 16;
  2408 	h = myabs(dy) + 16;
  2551 	h = abs(dy) + 16;
  2409 
  2552 
  2410 	if (TileVirtXY(thd->selstart.x, thd->selstart.y) == TileVirtXY(x, y)) { // check if we're only within one tile
  2553 	if (TileVirtXY(thd->selstart.x, thd->selstart.y) == TileVirtXY(x, y)) { // check if we're only within one tile
  2411 		if (method == VPM_RAILDIRS) {
  2554 		if (method == VPM_RAILDIRS) {
  2412 			b = GetAutorailHT(x, y);
  2555 			b = GetAutorailHT(x, y);
  2413 		} else { // rect for autosignals on one tile
  2556 		} else { // rect for autosignals on one tile
  2529  * Selects tiles while dragging
  2672  * Selects tiles while dragging
  2530  * @param x X coordinate of end of selection
  2673  * @param x X coordinate of end of selection
  2531  * @param y Y coordinate of end of selection
  2674  * @param y Y coordinate of end of selection
  2532  * @param method modifies the way tiles are selected. Possible
  2675  * @param method modifies the way tiles are selected. Possible
  2533  * methods are VPM_* in viewport.h */
  2676  * methods are VPM_* in viewport.h */
  2534 void VpSelectTilesWithMethod(int x, int y, int method)
  2677 void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method)
  2535 {
  2678 {
  2536 	int sx, sy;
  2679 	int sx, sy;
  2537 	HighLightStyle style;
  2680 	HighLightStyle style;
  2538 
  2681 
  2539 	if (x == -1) {
  2682 	if (x == -1) {
  2547 		_thd.selend.y = y;
  2690 		_thd.selend.y = y;
  2548 		CalcRaildirsDrawstyle(&_thd, x, y, method);
  2691 		CalcRaildirsDrawstyle(&_thd, x, y, method);
  2549 		return;
  2692 		return;
  2550 	}
  2693 	}
  2551 
  2694 
       
  2695 	/* Needed so level-land is placed correctly */
  2552 	if (_thd.next_drawstyle == HT_POINT) {
  2696 	if (_thd.next_drawstyle == HT_POINT) {
  2553 		x += TILE_SIZE / 2;
  2697 		x += TILE_SIZE / 2;
  2554 		y += TILE_SIZE / 2;
  2698 		y += TILE_SIZE / 2;
  2555 	}
  2699 	}
  2556 
  2700 
  2557 	sx = _thd.selstart.x;
  2701 	sx = _thd.selstart.x;
  2558 	sy = _thd.selstart.y;
  2702 	sy = _thd.selstart.y;
  2559 
  2703 
  2560 	switch (method) {
  2704 	switch (method) {
  2561 		case VPM_X_OR_Y: /* drag in X or Y direction */
  2705 		case VPM_X_OR_Y: /* drag in X or Y direction */
  2562 			if (myabs(sy - y) < myabs(sx - x)) {
  2706 			if (abs(sy - y) < abs(sx - x)) {
  2563 				y = sy;
  2707 				y = sy;
  2564 				style = HT_DIR_X;
  2708 				style = HT_DIR_X;
  2565 			} else {
  2709 			} else {
  2566 				x = sx;
  2710 				x = sx;
  2567 				style = HT_DIR_Y;
  2711 				style = HT_DIR_Y;
  2598 				GuiShowTooltipsWithArgs(measure_strings_length[index], index, params);
  2742 				GuiShowTooltipsWithArgs(measure_strings_length[index], index, params);
  2599 			} break;
  2743 			} break;
  2600 
  2744 
  2601 		case VPM_X_AND_Y_LIMITED: { /* drag an X by Y constrained rect area */
  2745 		case VPM_X_AND_Y_LIMITED: { /* drag an X by Y constrained rect area */
  2602 			int limit = (_thd.sizelimit - 1) * TILE_SIZE;
  2746 			int limit = (_thd.sizelimit - 1) * TILE_SIZE;
  2603 			x = sx + clamp(x - sx, -limit, limit);
  2747 			x = sx + Clamp(x - sx, -limit, limit);
  2604 			y = sy + clamp(y - sy, -limit, limit);
  2748 			y = sy + Clamp(y - sy, -limit, limit);
  2605 			} /* Fallthrough */
  2749 			} /* Fallthrough */
  2606 		case VPM_X_AND_Y: { /* drag an X by Y area */
  2750 		case VPM_X_AND_Y: { /* drag an X by Y area */
  2607 			if (_patches.measure_tooltip) {
  2751 			if (_patches.measure_tooltip) {
  2608 				static const StringID measure_strings_area[] = {
  2752 				static const StringID measure_strings_area[] = {
  2609 					STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
  2753 					STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
  2610 				};
  2754 				};
  2611 
  2755 
  2612 				TileIndex t0 = TileVirtXY(sx, sy);
  2756 				TileIndex t0 = TileVirtXY(sx, sy);
  2613 				TileIndex t1 = TileVirtXY(x, y);
  2757 				TileIndex t1 = TileVirtXY(x, y);
  2614 				uint dx = delta(TileX(t0), TileX(t1)) + 1;
  2758 				uint dx = Delta(TileX(t0), TileX(t1)) + 1;
  2615 				uint dy = delta(TileY(t0), TileY(t1)) + 1;
  2759 				uint dy = Delta(TileY(t0), TileY(t1)) + 1;
  2616 				byte index = 0;
  2760 				byte index = 0;
  2617 				uint64 params[3];
  2761 				uint64 params[3];
  2618 
  2762 
  2619 				/* If dragging an area (eg dynamite tool) and it is actually a single
  2763 				/* If dragging an area (eg dynamite tool) and it is actually a single
  2620 				 * row/column, change the type to 'line' to get proper calculation for height */
  2764 				 * row/column, change the type to 'line' to get proper calculation for height */
  2697 	w->wndproc(w, &e);
  2841 	w->wndproc(w, &e);
  2698 
  2842 
  2699 	return false;
  2843 	return false;
  2700 }
  2844 }
  2701 
  2845 
  2702 void SetObjectToPlaceWnd(CursorID icon, SpriteID pal, byte mode, Window *w)
  2846 void SetObjectToPlaceWnd(CursorID icon, SpriteID pal, ViewportHighlightMode mode, Window *w)
  2703 {
  2847 {
  2704 	SetObjectToPlace(icon, pal, mode, w->window_class, w->window_number);
  2848 	SetObjectToPlace(icon, pal, mode, w->window_class, w->window_number);
  2705 }
  2849 }
  2706 
  2850 
  2707 #include "table/animcursors.h"
  2851 #include "table/animcursors.h"
  2708 
  2852 
  2709 void SetObjectToPlace(CursorID icon, SpriteID pal, byte mode, WindowClass window_class, WindowNumber window_num)
  2853 void SetObjectToPlace(CursorID icon, SpriteID pal, ViewportHighlightMode mode, WindowClass window_class, WindowNumber window_num)
  2710 {
  2854 {
  2711 	Window *w;
  2855 	Window *w;
  2712 
  2856 
  2713 	/* undo clicking on button */
  2857 	/* undo clicking on button */
  2714 	if (_thd.place_mode != 0) {
  2858 	if (_thd.place_mode != VHM_NONE) {
  2715 		_thd.place_mode = 0;
  2859 		_thd.place_mode = VHM_NONE;
  2716 		w = FindWindowById(_thd.window_class, _thd.window_number);
  2860 		w = FindWindowById(_thd.window_class, _thd.window_number);
  2717 		if (w != NULL) CallWindowEventNP(w, WE_ABORT_PLACE_OBJ);
  2861 		if (w != NULL) CallWindowEventNP(w, WE_ABORT_PLACE_OBJ);
  2718 	}
  2862 	}
  2719 
  2863 
  2720 	SetTileSelectSize(1, 1);
  2864 	SetTileSelectSize(1, 1);
  2721 
  2865 
  2722 	_thd.make_square_red = false;
  2866 	_thd.make_square_red = false;
  2723 
  2867 
  2724 	if (mode == VHM_DRAG) { // mode 4 is for dragdropping trains in the depot window
  2868 	if (mode == VHM_DRAG) { // VHM_DRAG is for dragdropping trains in the depot window
  2725 		mode = 0;
  2869 		mode = VHM_NONE;
  2726 		_special_mouse_mode = WSM_DRAGDROP;
  2870 		_special_mouse_mode = WSM_DRAGDROP;
  2727 	} else {
  2871 	} else {
  2728 		_special_mouse_mode = WSM_NONE;
  2872 		_special_mouse_mode = WSM_NONE;
  2729 	}
  2873 	}
  2730 
  2874