src/viewport.cpp
changeset 7769 f072d6bb8b99
parent 7764 e594296e90f6
child 7824 5a63d41b59ea
equal deleted inserted replaced
7768:0ba14389a50e 7769:f072d6bb8b99
   107 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(StringSpriteToDraw));
   107 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(StringSpriteToDraw));
   108 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(TileSpriteToDraw));
   108 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(TileSpriteToDraw));
   109 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(ChildScreenSpriteToDraw));
   109 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(ChildScreenSpriteToDraw));
   110 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(ParentSpriteToDraw));
   110 assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(ParentSpriteToDraw));
   111 
   111 
       
   112 /* Enumeration of multi-part foundations */
       
   113 enum FoundationPart {
       
   114 	FOUNDATION_PART_NONE     = 0xFF,  ///< Neither foundation nor groundsprite drawn yet.
       
   115 	FOUNDATION_PART_NORMAL   = 0,     ///< First part (normal foundation or no foundation)
       
   116 	FOUNDATION_PART_HALFTILE = 1,     ///< Second part (halftile foundation)
       
   117 	FOUNDATION_PART_END
       
   118 };
       
   119 
   112 struct ViewportDrawer {
   120 struct ViewportDrawer {
   113 	DrawPixelInfo dpi;
   121 	DrawPixelInfo dpi;
   114 
   122 
   115 	byte *spritelist_mem;
   123 	byte *spritelist_mem;
   116 	const byte *eof_spritelist_mem;
   124 	const byte *eof_spritelist_mem;
   123 	ParentSpriteToDraw **parent_list;
   131 	ParentSpriteToDraw **parent_list;
   124 	ParentSpriteToDraw * const *eof_parent_list;
   132 	ParentSpriteToDraw * const *eof_parent_list;
   125 
   133 
   126 	byte combine_sprites;
   134 	byte combine_sprites;
   127 
   135 
   128 	ParentSpriteToDraw *foundation;                      ///< Foundation sprite.
   136 	ParentSpriteToDraw *foundation[FOUNDATION_PART_END];                   ///< Foundation sprites.
   129 	ChildScreenSpriteToDraw **last_foundation_child;     ///< Tail of ChildSprite list of active foundation.
   137 	FoundationPart foundation_part;                                        ///< Currently active foundation for ground sprite drawing.
   130 	Point foundation_offset;                             ///< Pixeloffset of ground sprites on the foundation.
   138 	ChildScreenSpriteToDraw **last_foundation_child[FOUNDATION_PART_END];  ///< Tail of ChildSprite list of the foundations.
       
   139 	Point foundation_offset[FOUNDATION_PART_END];                          ///< Pixeloffset for ground sprites on the foundations.
   131 };
   140 };
   132 
   141 
   133 static ViewportDrawer *_cur_vd;
   142 static ViewportDrawer *_cur_vd;
   134 
   143 
   135 TileHighlightData _thd;
   144 TileHighlightData _thd;
   479  * The pixel offset of the sprite relative to the ParentSprite is the sum of the offset passed to OffsetGroundSprite() and extra_offs_?.
   488  * The pixel offset of the sprite relative to the ParentSprite is the sum of the offset passed to OffsetGroundSprite() and extra_offs_?.
   480  *
   489  *
   481  * @param image the image to draw.
   490  * @param image the image to draw.
   482  * @param pal the provided palette.
   491  * @param pal the provided palette.
   483  * @param sub Only draw a part of the sprite.
   492  * @param sub Only draw a part of the sprite.
       
   493  * @param foundation_part Foundation part.
   484  * @param extra_offs_x Pixel X offset for the sprite position.
   494  * @param extra_offs_x Pixel X offset for the sprite position.
   485  * @param extra_offs_y Pixel Y offset for the sprite position.
   495  * @param extra_offs_y Pixel Y offset for the sprite position.
   486  */
   496  */
   487 static void AddChildSpriteToFoundation(SpriteID image, SpriteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
   497 static void AddChildSpriteToFoundation(SpriteID image, SpriteID pal, const SubSprite *sub, FoundationPart foundation_part, int extra_offs_x, int extra_offs_y)
   488 {
   498 {
   489 	ViewportDrawer *vd = _cur_vd;
   499 	ViewportDrawer *vd = _cur_vd;
   490 	assert(vd->foundation != NULL);
   500 	assert(IS_INT_INSIDE(foundation_part, 0, FOUNDATION_PART_END));
   491 	Point offs = vd->foundation_offset;
   501 	assert(vd->foundation[foundation_part] != NULL);
       
   502 	Point offs = vd->foundation_offset[foundation_part];
   492 
   503 
   493 	/* Change the active ChildSprite list to the one of the foundation */
   504 	/* Change the active ChildSprite list to the one of the foundation */
   494 	ChildScreenSpriteToDraw **old_child = vd->last_child;
   505 	ChildScreenSpriteToDraw **old_child = vd->last_child;
   495 	vd->last_child = vd->last_foundation_child;
   506 	vd->last_child = vd->last_foundation_child[foundation_part];
   496 
   507 
   497 	AddChildSpriteScreen(image, pal, offs.x + extra_offs_x, offs.y + extra_offs_y, false, sub);
   508 	AddChildSpriteScreen(image, pal, offs.x + extra_offs_x, offs.y + extra_offs_y, false, sub);
   498 
   509 
   499 	/* Switch back to last ChildSprite list */
   510 	/* Switch back to last ChildSprite list */
   500 	vd->last_child = old_child;
   511 	vd->last_child = old_child;
   509  * @param sub Only draw a part of the sprite.
   520  * @param sub Only draw a part of the sprite.
   510  */
   521  */
   511 void DrawGroundSprite(SpriteID image, SpriteID pal, const SubSprite *sub)
   522 void DrawGroundSprite(SpriteID image, SpriteID pal, const SubSprite *sub)
   512 {
   523 {
   513 	ViewportDrawer *vd = _cur_vd;
   524 	ViewportDrawer *vd = _cur_vd;
   514 	if (vd->foundation != NULL) {
   525 	/* Switch to first foundation part, if no foundation was drawn */
   515 		AddChildSpriteToFoundation(image, pal, sub, 0, 0);
   526 	if (vd->foundation_part == FOUNDATION_PART_NONE) vd->foundation_part = FOUNDATION_PART_NORMAL;
       
   527 
       
   528 	if (vd->foundation[vd->foundation_part] != NULL) {
       
   529 		AddChildSpriteToFoundation(image, pal, sub, vd->foundation_part, 0, 0);
   516 	} else {
   530 	} else {
   517 		DrawGroundSpriteAt(image, pal, _cur_ti->x, _cur_ti->y, _cur_ti->z, sub);
   531 		DrawGroundSpriteAt(image, pal, _cur_ti->x, _cur_ti->y, _cur_ti->z, sub);
   518 	}
   532 	}
   519 }
   533 }
   520 
   534 
   527  * @param y sprite y-offset (screen coordinates) of ground sprites relative to the "foundation"-ParentSprite.
   541  * @param y sprite y-offset (screen coordinates) of ground sprites relative to the "foundation"-ParentSprite.
   528  */
   542  */
   529 void OffsetGroundSprite(int x, int y)
   543 void OffsetGroundSprite(int x, int y)
   530 {
   544 {
   531 	ViewportDrawer *vd = _cur_vd;
   545 	ViewportDrawer *vd = _cur_vd;
   532 	assert(vd->foundation == NULL);
   546 	/* Switch to next foundation part */
       
   547 	switch (vd->foundation_part) {
       
   548 		case FOUNDATION_PART_NONE:
       
   549 			vd->foundation_part = FOUNDATION_PART_NORMAL;
       
   550 			break;
       
   551 		case FOUNDATION_PART_NORMAL:
       
   552 			vd->foundation_part = FOUNDATION_PART_HALFTILE;
       
   553 			break;
       
   554 		default: NOT_REACHED();
       
   555 	}
   533 
   556 
   534 	/* vd->last_child == NULL if foundation sprite was clipped by the viewport bounds */
   557 	/* vd->last_child == NULL if foundation sprite was clipped by the viewport bounds */
   535 	if (vd->last_child != NULL) vd->foundation = vd->parent_list[-1];
   558 	if (vd->last_child != NULL) vd->foundation[vd->foundation_part] = vd->parent_list[-1];
   536 
   559 
   537 	vd->foundation_offset.x = x;
   560 	vd->foundation_offset[vd->foundation_part].x = x;
   538 	vd->foundation_offset.y = y;
   561 	vd->foundation_offset[vd->foundation_part].y = y;
   539 	vd->last_foundation_child = vd->last_child;
   562 	vd->last_foundation_child[vd->foundation_part] = vd->last_child;
   540 }
   563 }
   541 
   564 
   542 /**
   565 /**
   543  * Adds a child sprite to a parent sprite.
   566  * Adds a child sprite to a parent sprite.
   544  * In contrast to "AddChildSpriteScreen()" the sprite position is in world coordinates
   567  * In contrast to "AddChildSpriteScreen()" the sprite position is in world coordinates
   728 	vd->spritelist_mem += sizeof(ChildScreenSpriteToDraw);
   751 	vd->spritelist_mem += sizeof(ChildScreenSpriteToDraw);
   729 
   752 
   730 	/* Append the sprite to the active ChildSprite list.
   753 	/* Append the sprite to the active ChildSprite list.
   731 	 * If the active ParentSprite is a foundation, update last_foundation_child as well. */
   754 	 * If the active ParentSprite is a foundation, update last_foundation_child as well. */
   732 	*vd->last_child = cs;
   755 	*vd->last_child = cs;
   733 	if (vd->last_foundation_child == vd->last_child) vd->last_foundation_child = &cs->next;
   756 	if (vd->last_foundation_child[0] == vd->last_child) vd->last_foundation_child[0] = &cs->next;
       
   757 	if (vd->last_foundation_child[1] == vd->last_child) vd->last_foundation_child[1] = &cs->next;
   734 	vd->last_child = &cs->next;
   758 	vd->last_child = &cs->next;
   735 
   759 
   736 	cs->image = image;
   760 	cs->image = image;
   737 	cs->pal = pal;
   761 	cs->pal = pal;
   738 	cs->sub = sub;
   762 	cs->sub = sub;
   777  *
   801  *
   778  * @param image the image to draw.
   802  * @param image the image to draw.
   779  * @param pal the provided palette.
   803  * @param pal the provided palette.
   780  * @param ti TileInfo Tile that is being drawn
   804  * @param ti TileInfo Tile that is being drawn
   781  * @param z_offset Z offset relative to the groundsprite. Only used for the sprite position, not for sprite sorting.
   805  * @param z_offset Z offset relative to the groundsprite. Only used for the sprite position, not for sprite sorting.
       
   806  * @param foundation_part Foundation part the sprite belongs to.
   782  */
   807  */
   783 static void DrawSelectionSprite(SpriteID image, SpriteID pal, const TileInfo *ti, int z_offset)
   808 static void DrawSelectionSprite(SpriteID image, SpriteID pal, const TileInfo *ti, int z_offset, FoundationPart foundation_part)
   784 {
   809 {
   785 	/* FIXME: This is not totally valid for some autorail highlights, that extent over the edges of the tile. */
   810 	/* FIXME: This is not totally valid for some autorail highlights, that extent over the edges of the tile. */
   786 	if (_cur_vd->foundation == NULL) {
   811 	if (_cur_vd->foundation[foundation_part] == NULL) {
   787 		/* draw on real ground */
   812 		/* draw on real ground */
   788 		DrawGroundSpriteAt(image, pal, ti->x, ti->y, ti->z + z_offset);
   813 		DrawGroundSpriteAt(image, pal, ti->x, ti->y, ti->z + z_offset);
   789 	} else {
   814 	} else {
   790 		/* draw on top of foundation */
   815 		/* draw on top of foundation */
   791 		AddChildSpriteToFoundation(image, pal, NULL, 0, -z_offset);
   816 		AddChildSpriteToFoundation(image, pal, NULL, foundation_part, 0, -z_offset);
   792 	}
   817 	}
   793 }
   818 }
   794 
   819 
   795 /**
   820 /**
   796  * Draws a selection rectangle on a tile.
   821  * Draws a selection rectangle on a tile.
   798  * @param ti TileInfo Tile that is being drawn
   823  * @param ti TileInfo Tile that is being drawn
   799  * @param pal Palette to apply.
   824  * @param pal Palette to apply.
   800  */
   825  */
   801 static void DrawTileSelectionRect(const TileInfo *ti, SpriteID pal)
   826 static void DrawTileSelectionRect(const TileInfo *ti, SpriteID pal)
   802 {
   827 {
   803 	DrawSelectionSprite(SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh], pal, ti, 7);
   828 	SpriteID sel;
       
   829 	if (IsHalftileSlope(ti->tileh)) {
       
   830 		Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
       
   831 		SpriteID sel2 = SPR_HALFTILE_SELECTION_FLAT + halftile_corner;
       
   832 		DrawSelectionSprite(sel2, pal, ti, 7 + TILE_HEIGHT, FOUNDATION_PART_HALFTILE);
       
   833 
       
   834 		Corner opposite_corner = OppositeCorner(halftile_corner);
       
   835 		if (IsSteepSlope(ti->tileh)) {
       
   836 			sel = SPR_HALFTILE_SELECTION_DOWN;
       
   837 		} else {
       
   838 			sel = ((ti->tileh & SlopeWithOneCornerRaised(opposite_corner)) != 0 ? SPR_HALFTILE_SELECTION_UP : SPR_HALFTILE_SELECTION_FLAT);
       
   839 		}
       
   840 		sel += opposite_corner;
       
   841 	} else {
       
   842 		sel = SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh];
       
   843 	}
       
   844 	DrawSelectionSprite(sel, pal, ti, 7, FOUNDATION_PART_NORMAL);
   804 }
   845 }
   805 
   846 
   806 static bool IsPartOfAutoLine(int px, int py)
   847 static bool IsPartOfAutoLine(int px, int py)
   807 {
   848 {
   808 	px -= _thd.selstart.x;
   849 	px -= _thd.selstart.x;
   845 {
   886 {
   846 	SpriteID image;
   887 	SpriteID image;
   847 	SpriteID pal;
   888 	SpriteID pal;
   848 	int offset;
   889 	int offset;
   849 
   890 
   850 	offset = _AutorailTilehSprite[ti->tileh][autorail_type];
   891 	FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
       
   892 	Slope autorail_tileh = (Slope)(ti->tileh & ~SLOPE_HALFTILE_MASK);
       
   893 	if (IsHalftileSlope(ti->tileh)) {
       
   894 		static const uint _lower_rail[4] = { 5U, 2U, 4U, 3U };
       
   895 		Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
       
   896 		if (autorail_type != _lower_rail[halftile_corner]) {
       
   897 			foundation_part = FOUNDATION_PART_HALFTILE;
       
   898 			/* Here we draw the highlights of the "three-corners-raised"-slope. That looks ok to me. */
       
   899 			autorail_tileh = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
       
   900 		}
       
   901 	}
       
   902 
       
   903 	offset = _AutorailTilehSprite[autorail_tileh][autorail_type];
   851 	if (offset >= 0) {
   904 	if (offset >= 0) {
   852 		image = SPR_AUTORAIL_BASE + offset;
   905 		image = SPR_AUTORAIL_BASE + offset;
   853 		pal = PAL_NONE;
   906 		pal = PAL_NONE;
   854 	} else {
   907 	} else {
   855 		image = SPR_AUTORAIL_BASE - offset;
   908 		image = SPR_AUTORAIL_BASE - offset;
   856 		pal = PALETTE_SEL_TILE_RED;
   909 		pal = PALETTE_SEL_TILE_RED;
   857 	}
   910 	}
   858 
   911 
   859 	DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7);
   912 	DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7, foundation_part);
   860 }
   913 }
   861 
   914 
   862 /**
   915 /**
   863  * Checks if the specified tile is selected and if so draws selection using correct selectionstyle.
   916  * Checks if the specified tile is selected and if so draws selection using correct selectionstyle.
   864  * @param *ti TileInfo Tile that is being drawn
   917  * @param *ti TileInfo Tile that is being drawn
   880 		if (_thd.drawstyle & HT_RECT) {
   933 		if (_thd.drawstyle & HT_RECT) {
   881 			DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE);
   934 			DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE);
   882 		} else if (_thd.drawstyle & HT_POINT) {
   935 		} else if (_thd.drawstyle & HT_POINT) {
   883 			/* Figure out the Z coordinate for the single dot. */
   936 			/* Figure out the Z coordinate for the single dot. */
   884 			byte z = 0;
   937 			byte z = 0;
       
   938 			FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
   885 			if (ti->tileh & SLOPE_N) {
   939 			if (ti->tileh & SLOPE_N) {
   886 				z += TILE_HEIGHT;
   940 				z += TILE_HEIGHT;
   887 				if (ti->tileh == SLOPE_STEEP_N) z += TILE_HEIGHT;
   941 				if ((ti->tileh & ~SLOPE_HALFTILE_MASK) == SLOPE_STEEP_N) z += TILE_HEIGHT;
   888 			}
   942 			}
   889 			DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti, z);
   943 			if (IsHalftileSlope(ti->tileh)) {
       
   944 				Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
       
   945 				if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT;
       
   946 				if (halftile_corner != CORNER_S) {
       
   947 					foundation_part = FOUNDATION_PART_HALFTILE;
       
   948 					if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT;
       
   949 				}
       
   950 			}
       
   951 			DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti, z, foundation_part);
   890 		} else if (_thd.drawstyle & HT_RAIL /*&& _thd.place_mode == VHM_RAIL*/) {
   952 		} else if (_thd.drawstyle & HT_RAIL /*&& _thd.place_mode == VHM_RAIL*/) {
   891 			/* autorail highlight piece under cursor */
   953 			/* autorail highlight piece under cursor */
   892 			uint type = _thd.drawstyle & 0xF;
   954 			uint type = _thd.drawstyle & 0xF;
   893 			assert(type <= 5);
   955 			assert(type <= 5);
   894 			DrawAutorailSelection(ti, _AutorailType[type][0]);
   956 			DrawAutorailSelection(ti, _AutorailType[type][0]);
   895 
       
   896 		} else if (IsPartOfAutoLine(ti->x, ti->y)) {
   957 		} else if (IsPartOfAutoLine(ti->x, ti->y)) {
   897 			/* autorail highlighting long line */
   958 			/* autorail highlighting long line */
   898 			int dir = _thd.drawstyle & ~0xF0;
   959 			int dir = _thd.drawstyle & ~0xF0;
   899 			uint side;
   960 			uint side;
   900 
   961 
   971 			}
  1032 			}
   972 
  1033 
   973 			y_cur += 0x10;
  1034 			y_cur += 0x10;
   974 			x_cur -= 0x10;
  1035 			x_cur -= 0x10;
   975 
  1036 
   976 			vd->foundation = NULL;
  1037 			vd->foundation_part = FOUNDATION_PART_NONE;
   977 			vd->last_foundation_child = NULL;
  1038 			vd->foundation[0] = NULL;
       
  1039 			vd->foundation[1] = NULL;
       
  1040 			vd->last_foundation_child[0] = NULL;
       
  1041 			vd->last_foundation_child[1] = NULL;
   978 
  1042 
   979 			_tile_type_procs[tt]->draw_tile_proc(&ti);
  1043 			_tile_type_procs[tt]->draw_tile_proc(&ti);
   980 			DrawTileSelection(&ti);
  1044 			DrawTileSelection(&ti);
   981 		} while (--width_cur);
  1045 		} while (--width_cur);
   982 
  1046