src/water_cmd.cpp
changeset 8876 a0b7938c6082
parent 8864 0082e49e8351
child 8882 f54bd3d64027
equal deleted inserted replaced
8875:ffcab947226b 8876:a0b7938c6082
    31 #include "vehicle_func.h"
    31 #include "vehicle_func.h"
    32 #include "sound_func.h"
    32 #include "sound_func.h"
    33 #include "variables.h"
    33 #include "variables.h"
    34 #include "player_func.h"
    34 #include "player_func.h"
    35 #include "settings_type.h"
    35 #include "settings_type.h"
       
    36 #include "clear_map.h"
    36 
    37 
    37 #include "table/sprites.h"
    38 #include "table/sprites.h"
    38 #include "table/strings.h"
    39 #include "table/strings.h"
    39 
    40 
    40 static Vehicle *FindFloodableVehicleOnTile(TileIndex tile);
    41 /**
    41 static void FloodVehicle(Vehicle *v);
    42  * Describes the behaviour of a tile during flooding.
       
    43  */
       
    44 enum FloodingBehaviour {
       
    45 	FLOOD_NONE,    ///< The tile does not flood neighboured tiles.
       
    46 	FLOOD_ACTIVE,  ///< The tile floods neighboured tiles.
       
    47 	FLOOD_PASSIVE, ///< The tile does not actively flood neighboured tiles, but it prevents them from drying up.
       
    48 	FLOOD_DRYUP,   ///< The tile drys up if it is not constantly flooded from neighboured tiles.
       
    49 };
       
    50 
       
    51 /**
       
    52  * Describes from which directions a specific slope can be flooded (if the tile is floodable at all).
       
    53  */
       
    54 static const uint8 _flood_from_dirs[] = {
       
    55 	(1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE), // SLOPE_FLAT
       
    56 	(1 << DIR_NE) | (1 << DIR_SE),                                 // SLOPE_W
       
    57 	(1 << DIR_NW) | (1 << DIR_NE),                                 // SLOPE_S
       
    58 	(1 << DIR_NE),                                                 // SLOPE_SW
       
    59 	(1 << DIR_NW) | (1 << DIR_SW),                                 // SLOPE_E
       
    60 	0,                                                             // SLOPE_EW
       
    61 	(1 << DIR_NW),                                                 // SLOPE_SE
       
    62 	(1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE),                 // SLOPE_WSE, SLOPE_STEEP_S
       
    63 	(1 << DIR_SW) | (1 << DIR_SE),                                 // SLOPE_N
       
    64 	(1 << DIR_SE),                                                 // SLOPE_NW
       
    65 	0,                                                             // SLOPE_NS
       
    66 	(1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE),                 // SLOPE_NWS, SLOPE_STEEP_W
       
    67 	(1 << DIR_SW),                                                 // SLOPE_NE
       
    68 	(1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE),                 // SLOPE_ENW, SLOPE_STEEP_N
       
    69 	(1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW),                 // SLOPE_SEN, SLOPE_STEEP_E
       
    70 };
       
    71 
       
    72 /**
       
    73  * Slopes that contain flat water and not only shore.
       
    74  */
       
    75 static const uint32 _active_water_slopes = (1 << SLOPE_FLAT) | (1 << SLOPE_W) | (1 << SLOPE_S) | (1 << SLOPE_E) | (1 << SLOPE_N);
    42 
    76 
    43 /**
    77 /**
    44  * Makes a tile canal or water depending on the surroundings.
    78  * Makes a tile canal or water depending on the surroundings.
    45  * This as for example docks and shipdepots do not store
    79  * This as for example docks and shipdepots do not store
    46  * whether the tile used to be canal or 'normal' water.
    80  * whether the tile used to be canal or 'normal' water.
   518 	/* Draw canal dikes if there are no river edges to draw. */
   552 	/* Draw canal dikes if there are no river edges to draw. */
   519 	if (edges_base <= 48) edges_base = SPR_CANAL_DIKES_BASE;
   553 	if (edges_base <= 48) edges_base = SPR_CANAL_DIKES_BASE;
   520 	DrawWaterEdges(edges_base, ti->tile);
   554 	DrawWaterEdges(edges_base, ti->tile);
   521 }
   555 }
   522 
   556 
       
   557 void DrawShoreTile(Slope tileh)
       
   558 {
       
   559 	/* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
       
   560 	 * This allows to calculate the proper sprite to display for this Slope */
       
   561 	static const byte tileh_to_shoresprite[32] = {
       
   562 		0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
       
   563 		0, 0, 0, 0, 0,  0, 0, 0, 0, 0,  0,  5,  0, 10, 15, 0,
       
   564 	};
       
   565 
       
   566 	assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
       
   567 	assert(tileh != SLOPE_FLAT);     // Shore is never flat
       
   568 
       
   569 	assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
       
   570 
       
   571 	DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
       
   572 }
       
   573 
   523 static void DrawTile_Water(TileInfo *ti)
   574 static void DrawTile_Water(TileInfo *ti)
   524 {
   575 {
   525 	switch (GetWaterTileType(ti->tile)) {
   576 	switch (GetWaterTileType(ti->tile)) {
   526 		case WATER_TILE_CLEAR:
   577 		case WATER_TILE_CLEAR:
   527 			DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
   578 			DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
   528 			if (IsCanal(ti->tile)) DrawCanalWater(ti->tile);
   579 			if (IsCanal(ti->tile)) DrawCanalWater(ti->tile);
   529 			DrawBridgeMiddle(ti);
   580 			DrawBridgeMiddle(ti);
   530 			break;
   581 			break;
   531 
   582 
   532 		case WATER_TILE_COAST: {
   583 		case WATER_TILE_COAST: {
   533 			/* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
   584 			DrawShoreTile(ti->tileh);
   534 			 * This allows to calculate the proper sprite to display for this Slope */
       
   535 			static const byte tileh_to_shoresprite[32] = {
       
   536 				0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
       
   537 				0, 0, 0, 0, 0,  0, 0, 0, 0, 0,  0,  5,  0, 10, 15, 0,
       
   538 			};
       
   539 
       
   540 			assert(!IsSteepSlope(ti->tileh));
       
   541 			DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[ti->tileh], PAL_NONE);
       
   542 			DrawBridgeMiddle(ti);
   585 			DrawBridgeMiddle(ti);
   543 		} break;
   586 		} break;
   544 
   587 
   545 		case WATER_TILE_LOCK: {
   588 		case WATER_TILE_LOCK: {
   546 			const WaterDrawTileStruct *t = _shiplift_display_seq[GetSection(ti->tile)];
   589 			const WaterDrawTileStruct *t = _shiplift_display_seq[GetSection(ti->tile)];
   623 static inline void MarkTileDirtyIfCanal(TileIndex tile)
   666 static inline void MarkTileDirtyIfCanal(TileIndex tile)
   624 {
   667 {
   625 	if (IsTileType(tile, MP_WATER) && IsCanal(tile)) MarkTileDirtyByTile(tile);
   668 	if (IsTileType(tile, MP_WATER) && IsCanal(tile)) MarkTileDirtyByTile(tile);
   626 }
   669 }
   627 
   670 
   628 /**
       
   629  * Floods neighboured floodable tiles
       
   630  *
       
   631  * @param tile The water source tile that causes the flooding.
       
   632  * @param offs[0] Destination tile to flood.
       
   633  * @param offs[1] First corner of edge between source and dest tile.
       
   634  * @param offs[2] Second corder of edge between source and dest tile.
       
   635  * @param offs[3] Third corner of dest tile.
       
   636  * @param offs[4] Fourth corner of dest tile.
       
   637  */
       
   638 static void TileLoopWaterHelper(TileIndex tile, const TileIndexDiffC *offs)
       
   639 {
       
   640 	TileIndex target = TILE_ADD(tile, ToTileIndexDiff(offs[0]));
       
   641 
       
   642 	/* type of this tile mustn't be water already. */
       
   643 	if (IsTileType(target, MP_WATER)) return;
       
   644 
       
   645 	/* Are both corners of the edge between source and dest on height 0 ? */
       
   646 	if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[1]))) != 0 ||
       
   647 			TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[2]))) != 0) {
       
   648 		return;
       
   649 	}
       
   650 
       
   651 	bool flooded = false; // Will be set to true, when something is flooded
       
   652 
       
   653 	/* Is any corner of the dest tile raised? (First two corners already checked above. */
       
   654 	if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[3]))) != 0 ||
       
   655 			TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[4]))) != 0) {
       
   656 		/* make coast.. */
       
   657 		switch (GetTileType(target)) {
       
   658 			case MP_RAILWAY: {
       
   659 				if (!IsPlainRailTile(target)) break;
       
   660 
       
   661 				flooded = FloodHalftile(target);
       
   662 
       
   663 				Vehicle *v = FindFloodableVehicleOnTile(target);
       
   664 				if (v != NULL) FloodVehicle(v);
       
   665 
       
   666 				break;
       
   667 			}
       
   668 
       
   669 			case MP_CLEAR:
       
   670 			case MP_TREES:
       
   671 				_current_player = OWNER_WATER;
       
   672 				if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
       
   673 					flooded = true;
       
   674 					MakeShore(target);
       
   675 					MarkTileDirtyByTile(target);
       
   676 				}
       
   677 				break;
       
   678 
       
   679 			default:
       
   680 				break;
       
   681 		}
       
   682 	} else {
       
   683 		/* Flood vehicles */
       
   684 		_current_player = OWNER_WATER;
       
   685 
       
   686 		Vehicle *v = FindFloodableVehicleOnTile(target);
       
   687 		if (v != NULL) FloodVehicle(v);
       
   688 
       
   689 		/* flood flat tile */
       
   690 		if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
       
   691 			flooded = true;
       
   692 			MakeWater(target);
       
   693 			MarkTileDirtyByTile(target);
       
   694 		}
       
   695 	}
       
   696 
       
   697 	if (flooded) {
       
   698 		/* Mark surrounding canal tiles dirty too to avoid glitches */
       
   699 		for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
       
   700 			MarkTileDirtyIfCanal(target + TileOffsByDir(dir));
       
   701 		}
       
   702 		/* update signals if needed */
       
   703 		UpdateSignalsInBuffer();
       
   704 	}
       
   705 }
       
   706 
   671 
   707 /**
   672 /**
   708  * Finds a vehicle to flood.
   673  * Finds a vehicle to flood.
   709  * It does not find vehicles that are already crashed on bridges, i.e. flooded.
   674  * It does not find vehicles that are already crashed on bridges, i.e. flooded.
   710  * @param tile the tile where to find a vehicle to flood
   675  * @param tile the tile where to find a vehicle to flood
   825 		SndPlayVehicleFx(SND_12_EXPLOSION, v);
   790 		SndPlayVehicleFx(SND_12_EXPLOSION, v);
   826 	}
   791 	}
   827 }
   792 }
   828 
   793 
   829 /**
   794 /**
       
   795  * Returns the behaviour of a tile during flooding.
       
   796  *
       
   797  * @return Behaviour of the tile
       
   798  */
       
   799 static FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
       
   800 {
       
   801 	/* FLOOD_ACTIVE:  'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, rail with flooded halftile
       
   802 	 * FLOOD_DRYUP:   coast with more than one corner raised
       
   803 	 * FLOOD_PASSIVE: oilrig, dock, water-industries
       
   804 	 * FLOOD_NONE:    canals, rivers, everything else
       
   805 	 */
       
   806 	switch (GetTileType(tile)) {
       
   807 		case MP_WATER:
       
   808 			if (IsCoast(tile)) {
       
   809 				Slope tileh = GetTileSlope(tile, NULL);
       
   810 				return (HasBit(_active_water_slopes, tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
       
   811 			} else {
       
   812 				return ((IsSea(tile) || (IsShipDepot(tile) && (GetShipDepotWaterOwner(tile) == OWNER_WATER))) ? FLOOD_ACTIVE : FLOOD_NONE);
       
   813 			}
       
   814 
       
   815 		case MP_RAILWAY:
       
   816 			return ((GetRailGroundType(tile) == RAIL_GROUND_WATER) ? FLOOD_ACTIVE : FLOOD_NONE);
       
   817 
       
   818 		case MP_STATION:
       
   819 			if (IsSeaBuoyTile(tile)) return FLOOD_ACTIVE;
       
   820 			if (IsOilRig(tile) || IsDock(tile)) return FLOOD_PASSIVE;
       
   821 			return FLOOD_NONE;
       
   822 
       
   823 		case MP_INDUSTRY:
       
   824 			return ((GetIndustrySpec(GetIndustryType(tile))->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0 ? FLOOD_PASSIVE : FLOOD_NONE);
       
   825 
       
   826 		default:
       
   827 			return FLOOD_NONE;
       
   828 	}
       
   829 }
       
   830 
       
   831 /**
       
   832  * Floods a tile.
       
   833  */
       
   834 static void DoFloodTile(TileIndex target)
       
   835 {
       
   836 	if (IsTileType(target, MP_WATER)) return;
       
   837 
       
   838 	bool flooded = false; // Will be set to true if something is changed.
       
   839 
       
   840 	_current_player = OWNER_WATER;
       
   841 
       
   842 	if (GetTileSlope(target, NULL) != SLOPE_FLAT) {
       
   843 		/* make coast.. */
       
   844 		switch (GetTileType(target)) {
       
   845 			case MP_RAILWAY: {
       
   846 				if (!IsPlainRailTile(target)) break;
       
   847 
       
   848 				flooded = FloodHalftile(target);
       
   849 
       
   850 				Vehicle *v = FindFloodableVehicleOnTile(target);
       
   851 				if (v != NULL) FloodVehicle(v);
       
   852 
       
   853 				break;
       
   854 			}
       
   855 
       
   856 			case MP_CLEAR:
       
   857 			case MP_TREES:
       
   858 				if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
       
   859 					MakeShore(target);
       
   860 					MarkTileDirtyByTile(target);
       
   861 					flooded = true;
       
   862 				}
       
   863 				break;
       
   864 
       
   865 			default:
       
   866 				break;
       
   867 		}
       
   868 	} else {
       
   869 		/* Flood vehicles */
       
   870 		Vehicle *v = FindFloodableVehicleOnTile(target);
       
   871 		if (v != NULL) FloodVehicle(v);
       
   872 
       
   873 		/* flood flat tile */
       
   874 		if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
       
   875 			MakeWater(target);
       
   876 			MarkTileDirtyByTile(target);
       
   877 			flooded = true;
       
   878 		}
       
   879 	}
       
   880 
       
   881 	if (flooded) {
       
   882 		/* Mark surrounding canal tiles dirty too to avoid glitches */
       
   883 		for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
       
   884 			MarkTileDirtyIfCanal(target + TileOffsByDir(dir));
       
   885 		}
       
   886 
       
   887 		/* update signals if needed */
       
   888 		UpdateSignalsInBuffer();
       
   889 	}
       
   890 
       
   891 	_current_player = OWNER_NONE;
       
   892 }
       
   893 
       
   894 /**
       
   895  * Drys a tile up.
       
   896  */
       
   897 static void DoDryUp(TileIndex tile)
       
   898 {
       
   899 	assert(IsTileType(tile, MP_WATER) && IsCoast(tile));
       
   900 	_current_player = OWNER_WATER;
       
   901 
       
   902 	if (CmdSucceeded(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
       
   903 		MakeClear(tile, CLEAR_GRASS, 3);
       
   904 		MarkTileDirtyByTile(tile);
       
   905 	}
       
   906 
       
   907 	_current_player = OWNER_NONE;
       
   908 }
       
   909 
       
   910 /**
   830  * Let a water tile floods its diagonal adjoining tiles
   911  * Let a water tile floods its diagonal adjoining tiles
   831  * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
   912  * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
   832  *
   913  *
   833  * @param tile the water/shore tile that floods
   914  * @param tile the water/shore tile that floods
   834  */
   915  */
   835 void TileLoop_Water(TileIndex tile)
   916 void TileLoop_Water(TileIndex tile)
   836 {
   917 {
   837 	static const TileIndexDiffC _tile_loop_offs_array[][5] = {
   918 	switch (GetFloodingBehaviour(tile)) {
   838 		// tile to mod              shore?    shore?
   919 		case FLOOD_ACTIVE:
   839 		{{-1,  0}, {0, 0}, {0, 1}, {-1,  0}, {-1,  1}},
   920 			for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
   840 		{{ 0,  1}, {0, 1}, {1, 1}, { 0,  2}, { 1,  2}},
   921 				TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
   841 		{{ 1,  0}, {1, 0}, {1, 1}, { 2,  0}, { 2,  1}},
   922 				if (dest == INVALID_TILE) continue;
   842 		{{ 0, -1}, {0, 0}, {1, 0}, { 0, -1}, { 1, -1}}
   923 
   843 	};
   924 				uint z_dest;
   844 
   925 				Slope slope_dest = (Slope)(GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP);
   845 	/* Ensure buoys on canal borders do not flood */
   926 				if (z_dest > 0) continue;
   846 	if (IsCanalBuoyTile(tile)) return;
   927 
   847 	/* Ensure only sea and coast floods, not canals or rivers */
   928 				if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
   848 	if (IsTileType(tile, MP_WATER) && !(IsSea(tile) || IsCoast(tile))) return;
   929 
   849 
   930 				DoFloodTile(dest);
   850 	/* floods in all four diagonal directions with the exception of the edges */
   931 			}
   851 	if (IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1) &&
   932 			break;
   852 			IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) {
   933 
   853 		uint i;
   934 		case FLOOD_DRYUP: {
   854 
   935 			Slope slope_here = (Slope)(GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP);
   855 		for (i = 0; i != lengthof(_tile_loop_offs_array); i++) {
   936 			uint check_dirs = _flood_from_dirs[slope_here];
   856 			TileLoopWaterHelper(tile, _tile_loop_offs_array[i]);
   937 			uint dir;
   857 		}
   938 			FOR_EACH_SET_BIT(dir, check_dirs) {
   858 	}
   939 				TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir));
   859 
   940 				if (dest == INVALID_TILE) continue;
   860 	/* _current_player can be changed by TileLoopWaterHelper.. reset it back here */
   941 
   861 	_current_player = OWNER_NONE;
   942 				FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
   862 
   943 				if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
   863 	/* edges */
   944 			}
   864 	if (TileX(tile) == 0 && IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) { //NE
   945 			DoDryUp(tile);
   865 		TileLoopWaterHelper(tile, _tile_loop_offs_array[2]);
   946 			break;
   866 	}
   947 		}
   867 
   948 
   868 	if (TileX(tile) == MapSizeX() - 2 && IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) { //SW
   949 		default: return;
   869 		TileLoopWaterHelper(tile, _tile_loop_offs_array[0]);
   950 	}
   870 	}
   951 }
   871 
   952 
   872 	if (TileY(tile) == 0 && IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1)) { //NW
   953 void ConvertGroundTilesIntoWaterTiles()
   873 		TileLoopWaterHelper(tile, _tile_loop_offs_array[1]);
   954 {
   874 	}
   955 	TileIndex tile;
   875 
   956 	uint z;
   876 	if (TileY(tile) == MapSizeY() - 2 && IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1)) { //SE
   957 	Slope slope;
   877 		TileLoopWaterHelper(tile, _tile_loop_offs_array[3]);
   958 
       
   959 	for (tile = 0; tile < MapSize(); ++tile) {
       
   960 		slope = GetTileSlope(tile, &z);
       
   961 		if (IsTileType(tile, MP_CLEAR) && z == 0) {
       
   962 			/* Make both water for tiles at level 0
       
   963 			 * and make shore, as that looks much better
       
   964 			 * during the generation. */
       
   965 			switch (slope) {
       
   966 				case SLOPE_FLAT:
       
   967 					MakeWater(tile);
       
   968 					break;
       
   969 
       
   970 				case SLOPE_N:
       
   971 				case SLOPE_E:
       
   972 				case SLOPE_S:
       
   973 				case SLOPE_W:
       
   974 					MakeShore(tile);
       
   975 					break;
       
   976 
       
   977 				default:
       
   978 					uint check_dirs = _flood_from_dirs[slope & ~SLOPE_STEEP];
       
   979 					uint dir;
       
   980 					FOR_EACH_SET_BIT(dir, check_dirs) {
       
   981 						TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
       
   982 						Slope slope_dest = (Slope)(GetTileSlope(dest, NULL) & ~SLOPE_STEEP);
       
   983 						if (HasBit(_active_water_slopes, slope_dest)) {
       
   984 							MakeShore(tile);
       
   985 							break;
       
   986 						}
       
   987 					}
       
   988 					break;
       
   989 			}
       
   990 		}
   878 	}
   991 	}
   879 }
   992 }
   880 
   993 
   881 static uint32 GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode)
   994 static uint32 GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode)
   882 {
   995 {