src/rail_cmd.cpp
branchNewGRF_ports
changeset 6871 5a9dc001e1ad
parent 6870 ca3fd1fbe311
child 6872 1c4a4a609f85
--- a/src/rail_cmd.cpp	Sat Oct 06 21:16:00 2007 +0000
+++ b/src/rail_cmd.cpp	Mon Dec 03 23:39:38 2007 +0000
@@ -40,6 +40,8 @@
 #include "train.h"
 #include "misc/autoptr.hpp"
 #include "autoslope.h"
+#include "transparency.h"
+#include "water.h"
 
 const byte _track_sloped_sprites[14] = {
 	14, 15, 22, 13,
@@ -150,10 +152,8 @@
 }
 
 
-static const TrackBits _valid_tileh_slopes[][15] = {
-
-/* set of normal ones */
-{
+/** Valid TrackBits on a specific (non-steep)-slope without foundation */
+static const TrackBits _valid_tracks_without_foundation[15] = {
 	TRACK_BIT_ALL,
 	TRACK_BIT_RIGHT,
 	TRACK_BIT_UPPER,
@@ -172,10 +172,10 @@
 	TRACK_BIT_X,
 	TRACK_BIT_UPPER,
 	TRACK_BIT_RIGHT,
-},
-
-/* allowed rail for an evenly raised platform */
-{
+};
+
+/** Valid TrackBits on a specific (non-steep)-slope with leveled foundation */
+static const TrackBits _valid_tracks_on_leveled_foundation[15] = {
 	TRACK_BIT_NONE,
 	TRACK_BIT_LEFT,
 	TRACK_BIT_LOWER,
@@ -194,62 +194,111 @@
 	TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_RIGHT,
 	TRACK_BIT_ALL,
 	TRACK_BIT_ALL
-}
 };
 
+/**
+ * Checks if a track combination is valid on a specific slope and returns the needed foundation.
+ *
+ * @param tileh Tile slope.
+ * @param bits  Trackbits.
+ * @return Needed foundation or FOUNDATION_INVALID if track/slope combination is not allowed.
+ */
 Foundation GetRailFoundation(Slope tileh, TrackBits bits)
 {
-	if (!IsSteepSlope(tileh)) {
-		if ((~_valid_tileh_slopes[0][tileh] & bits) == 0) return FOUNDATION_NONE;
-		if ((~_valid_tileh_slopes[1][tileh] & bits) == 0) return FOUNDATION_LEVELED;
-	}
-
-	switch (bits) {
-		default: NOT_REACHED();
-		case TRACK_BIT_X:     return FOUNDATION_INCLINED_X;
-		case TRACK_BIT_Y:     return FOUNDATION_INCLINED_Y;
-		case TRACK_BIT_LEFT:  return (tileh == SLOPE_STEEP_W ? FOUNDATION_STEEP_HIGHER : FOUNDATION_STEEP_LOWER);
-		case TRACK_BIT_LOWER: return (tileh == SLOPE_STEEP_S ? FOUNDATION_STEEP_HIGHER : FOUNDATION_STEEP_LOWER);
-		case TRACK_BIT_RIGHT: return (tileh == SLOPE_STEEP_E ? FOUNDATION_STEEP_HIGHER : FOUNDATION_STEEP_LOWER);
-		case TRACK_BIT_UPPER: return (tileh == SLOPE_STEEP_N ? FOUNDATION_STEEP_HIGHER : FOUNDATION_STEEP_LOWER);
+	if (bits == TRACK_BIT_NONE) return FOUNDATION_NONE;
+
+	if (IsSteepSlope(tileh)) {
+		/* Test for inclined foundations */
+		if (bits == TRACK_BIT_X) return FOUNDATION_INCLINED_X;
+		if (bits == TRACK_BIT_Y) return FOUNDATION_INCLINED_Y;
+
+		/* Get higher track */
+		Corner highest_corner = GetHighestSlopeCorner(tileh);
+		TrackBits higher_track = CornerToTrackBits(highest_corner);
+
+		/* Only higher track? */
+		if (bits == higher_track) return HalftileFoundation(highest_corner);
+
+		/* Overlap with higher track? */
+		if (TracksOverlap(bits | higher_track)) return FOUNDATION_INVALID;
+
+		/* either lower track or both higher and lower track */
+		return ((bits & higher_track) != 0 ? FOUNDATION_STEEP_BOTH : FOUNDATION_STEEP_LOWER);
+	} else {
+		if ((~_valid_tracks_without_foundation[tileh] & bits) == 0) return FOUNDATION_NONE;
+
+		bool valid_on_leveled = ((~_valid_tracks_on_leveled_foundation[tileh] & bits) == 0);
+
+		Corner track_corner;
+		switch (bits) {
+			case TRACK_BIT_LEFT:  track_corner = CORNER_W; break;
+			case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
+			case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
+			case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
+
+			case TRACK_BIT_HORZ:
+				if (tileh == SLOPE_N) return HalftileFoundation(CORNER_N);
+				if (tileh == SLOPE_S) return HalftileFoundation(CORNER_S);
+				return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
+
+			case TRACK_BIT_VERT:
+				if (tileh == SLOPE_W) return HalftileFoundation(CORNER_W);
+				if (tileh == SLOPE_E) return HalftileFoundation(CORNER_E);
+				return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
+
+			case TRACK_BIT_X:
+				if (HasSlopeHighestCorner(tileh)) return FOUNDATION_INCLINED_X;
+				return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
+
+			case TRACK_BIT_Y:
+				if (HasSlopeHighestCorner(tileh)) return FOUNDATION_INCLINED_Y;
+				return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
+
+			default:
+				return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
+		}
+		/* Single diagonal track */
+
+		/* Track must be at least valid on leveled foundation */
+		if (!valid_on_leveled) return FOUNDATION_INVALID;
+
+		/* If slope has three raised corners, build leveled foundation */
+		if (HasSlopeHighestCorner(ComplementSlope(tileh))) return FOUNDATION_LEVELED;
+
+		/* If neighboured corners of track_corner are lowered, build halftile foundation */
+		if ((tileh & SlopeWithThreeCornersRaised(OppositeCorner(track_corner))) == SlopeWithOneCornerRaised(track_corner)) return HalftileFoundation(track_corner);
+
+		/* else special anti-zig-zag foundation */
+		return SpecialRailFoundation(track_corner);
 	}
 }
 
 
+/**
+ * Tests if a track can be build on a tile.
+ *
+ * @param tileh Tile slope.
+ * @param rail_bits Tracks to build.
+ * @param existing Tracks already built.
+ * @param tile Tile (used for water test)
+ * @return Error message or cost for foundation building.
+ */
 static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
 {
-	if (IsSteepSlope(tileh)) {
-		if (_patches.build_on_slopes && existing == 0) {
-			/* There may only be one track on steep slopes. (Autoslope calls with multiple bits in rail_bits) */
-			if (KILL_FIRST_BIT(rail_bits & TRACK_BIT_MASK) == 0) {
-				TrackBits valid = TRACK_BIT_CROSS | (HASBIT(1 << SLOPE_STEEP_W | 1 << SLOPE_STEEP_E, tileh) ? TRACK_BIT_VERT : TRACK_BIT_HORZ);
-				if (valid & rail_bits) return _price.terraform;
-			}
-		}
-	} else {
-		rail_bits |= existing;
-
-		/* don't allow building on the lower side of a coast */
-		if (IsTileType(tile, MP_WATER) &&
-				~_valid_tileh_slopes[1][tileh] & rail_bits) {
-			return_cmd_error(STR_3807_CAN_T_BUILD_ON_WATER);
-		}
-
-		/* no special foundation */
-		if ((~_valid_tileh_slopes[0][tileh] & rail_bits) == 0) {
-			return CommandCost();
-		} else if (!_patches.build_on_slopes || _is_old_ai_player) {
-			return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
-		}
-
-		if ((~_valid_tileh_slopes[1][tileh] & rail_bits) == 0 || ( // whole tile is leveled up
-					(rail_bits == TRACK_BIT_X || rail_bits == TRACK_BIT_Y) &&
-					(tileh == SLOPE_W || tileh == SLOPE_S || tileh == SLOPE_E || tileh == SLOPE_N)
-				)) { // partly up
-			return CommandCost((existing != 0) ? 0 : _price.terraform);
-		}
+	/* don't allow building on the lower side of a coast */
+	if (IsTileType(tile, MP_WATER) || (IsTileType(tile, MP_RAILWAY) && (GetRailGroundType(tile) == RAIL_GROUND_WATER))) {
+		if (!IsSteepSlope(tileh) && ((~_valid_tracks_on_leveled_foundation[tileh] & (rail_bits | existing)) != 0)) return_cmd_error(STR_3807_CAN_T_BUILD_ON_WATER);
 	}
-	return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
+
+	Foundation f_new = GetRailFoundation(tileh, rail_bits | existing);
+
+	/* check track/slope combination */
+	if ((f_new == FOUNDATION_INVALID) ||
+	    ((f_new != FOUNDATION_NONE) && (!_patches.build_on_slopes || _is_old_ai_player))
+	   ) return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
+
+	Foundation f_old = GetRailFoundation(tileh, existing);
+	return CommandCost(f_new != f_old ? _price.terraform : (Money)0);
 }
 
 /* Validate functions for rail building */
@@ -312,7 +361,7 @@
 		case MP_ROAD:
 #define M(x) (1 << (x))
 			/* Level crossings may only be built on these slopes */
-			if (!HASBIT(M(SLOPE_SEN) | M(SLOPE_ENW) | M(SLOPE_NWS) | M(SLOPE_NS) | M(SLOPE_WSE) | M(SLOPE_EW) | M(SLOPE_FLAT), tileh)) {
+			if (!HasBit(M(SLOPE_SEN) | M(SLOPE_ENW) | M(SLOPE_NWS) | M(SLOPE_NS) | M(SLOPE_WSE) | M(SLOPE_EW) | M(SLOPE_FLAT), tileh)) {
 				return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
 			}
 #undef M
@@ -358,6 +407,8 @@
 			/* FALLTHROUGH */
 
 		default:
+			bool water_ground = IsTileType(tile, MP_WATER) && !IsSteepSlope(tileh) && HasSlopeHighestCorner(tileh);
+
 			ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile);
 			if (CmdFailed(ret)) return ret;
 			cost.AddCost(ret);
@@ -366,7 +417,15 @@
 			if (CmdFailed(ret)) return ret;
 			cost.AddCost(ret);
 
-			if (flags & DC_EXEC) MakeRailNormal(tile, _current_player, trackbit, railtype);
+			if (water_ground) {
+				cost.AddCost(-_price.clear_water);
+				cost.AddCost(_price.clear_roughland);
+			}
+
+			if (flags & DC_EXEC) {
+				MakeRailNormal(tile, _current_player, trackbit, railtype);
+				if (water_ground) SetRailGroundType(tile, RAIL_GROUND_WATER);
+			}
 			break;
 	}
 
@@ -376,7 +435,7 @@
 		YapfNotifyTrackLayoutChange(tile, track);
 	}
 
-	return cost.AddCost(_price.build_rail);
+	return cost.AddCost(RailBuildCost(railtype));
 }
 
 /** Remove a single piece of track
@@ -432,7 +491,11 @@
 			if (flags & DC_EXEC) {
 				present ^= trackbit;
 				if (present == 0) {
-					DoClearSquare(tile);
+					if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
+						MakeShore(tile);
+					} else {
+						DoClearSquare(tile);
+					}
 				} else {
 					SetTrackBits(tile, present);
 				}
@@ -464,6 +527,41 @@
 }
 
 
+/**
+ * Called from water_cmd if a non-flat rail-tile gets flooded and should be converted to shore.
+ * The function floods the lower halftile, if the tile has a halftile foundation.
+ *
+ * @param t The tile to flood.
+ */
+void FloodHalftile(TileIndex t)
+{
+	if (GetRailGroundType(t) == RAIL_GROUND_WATER) return;
+
+	Slope tileh = GetTileSlope(t, NULL);
+	TrackBits rail_bits = GetTrackBits(t);
+
+	if (!IsSteepSlope(tileh) && HasSlopeHighestCorner(tileh)) {
+		TrackBits lower_track = CornerToTrackBits(OppositeCorner(GetHighestSlopeCorner(tileh)));
+
+		TrackBits to_remove = lower_track & rail_bits;
+		if (to_remove != 0) {
+			_current_player = OWNER_WATER;
+			if (CmdFailed(DoCommand(t, 0, FIND_FIRST_BIT(to_remove), DC_EXEC, CMD_REMOVE_SINGLE_RAIL))) return; // not yet floodable
+			rail_bits = rail_bits & ~to_remove;
+			if (rail_bits == 0) {
+				MakeShore(t);
+				MarkTileDirtyByTile(t);
+				return;
+			}
+		}
+
+		if (IsNonContinuousFoundation(GetRailFoundation(tileh, rail_bits))) {
+			SetRailGroundType(t, RAIL_GROUND_WATER);
+			MarkTileDirtyByTile(t);
+		}
+	}
+}
+
 static const TileIndexDiffC _trackdelta[] = {
 	{ -1,  0 }, {  0,  1 }, { -1,  0 }, {  0,  1 }, {  1,  0 }, {  0,  1 },
 	{  0,  0 },
@@ -504,8 +602,8 @@
 		(trdy <= 0 && dy > 0) ||
 		(trdy >= 0 && dy < 0)
 	) {
-		if (!HASBIT(*trackdir, 3)) { // first direction is invalid, try the other
-			SetBitT(*trackdir, 3); // reverse the direction
+		if (!HasBit(*trackdir, 3)) { // first direction is invalid, try the other
+			SetBit(*trackdir, 3); // reverse the direction
 			trdx = -trdx;
 			trdy = -trdy;
 		} else { // other direction is invalid too, invalid drag
@@ -539,7 +637,7 @@
 	CommandCost ret, total_cost;
 	Track track = (Track)GB(p2, 4, 3);
 	Trackdir trackdir;
-	byte mode = HASBIT(p2, 7);
+	byte mode = HasBit(p2, 7);
 	RailType railtype = (RailType)GB(p2, 0, 4);
 	TileIndex end_tile;
 
@@ -569,7 +667,7 @@
 		tile += ToTileIndexDiff(_trackdelta[trackdir]);
 
 		/* toggle railbit for the non-diagonal tracks */
-		if (!IsDiagonalTrackdir(trackdir)) ToggleBitT(trackdir, 0);
+		if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
 	}
 
 	return (total_cost.GetCost() == 0) ? CMD_ERROR : total_cost;
@@ -588,7 +686,7 @@
  */
 CommandCost CmdBuildRailroadTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
-	return CmdRailTrackHelper(tile, flags, p1, CLRBIT(p2, 7));
+	return CmdRailTrackHelper(tile, flags, p1, ClrBit(p2, 7));
 }
 
 /** Build rail on a stretch of track.
@@ -604,7 +702,7 @@
  */
 CommandCost CmdRemoveRailroadTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
-	return CmdRailTrackHelper(tile, flags, p1, SETBIT(p2, 7));
+	return CmdRailTrackHelper(tile, flags, p1, SetBit(p2, 7));
 }
 
 /** Build a train depot
@@ -678,16 +776,20 @@
  * @param flags operation to perform
  * @param p1 various bitstuffed elements
  * - p1 = (bit 0-2) - track-orientation, valid values: 0-5 (Track enum)
- * - p1 = (bit 3)   - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
+ * - p1 = (bit 3)   - 1 = override signal/semaphore, or pre/exit/combo signal or (for bit 7) toggle variant (CTRL-toggle)
  * - p1 = (bit 4)   - 0 = signals, 1 = semaphores
+ * - p1 = (bit 5-6) - type of the signal, for valid values see enum SignalType in rail_map.h
+ * - p1 = (bit 7)   - convert the present signal type and variant
  * @param p2 used for CmdBuildManySignals() to copy direction of first signal
  * TODO: p2 should be replaced by two bits for "along" and "against" the track.
  */
 CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	Track track = (Track)GB(p1, 0, 3);
-	bool pre_signal = HASBIT(p1, 3);
-	SignalVariant sigvar = (pre_signal ^ HASBIT(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC;
+	bool ctrl_pressed = HasBit(p1, 3); // was the CTRL button pressed
+	SignalVariant sigvar = (ctrl_pressed ^ HasBit(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC; // the signal variant of the new signal
+	SignalType sigtype = (SignalType)GB(p1, 5, 2); // the signal type of the new signal
+	bool convert_signal = HasBit(p1, 7); // convert button pressed
 	CommandCost cost;
 
 	if (!ValParamTrackOrientation(track) || !IsTileType(tile, MP_RAILWAY) || !EnsureNoTrainOnTrack(tile, track))
@@ -701,18 +803,19 @@
 
 	if (!CheckTileOwnership(tile)) return CMD_ERROR;
 
-	_error_message = STR_1005_NO_SUITABLE_RAILROAD_TRACK;
-
 	{
 		/* See if this is a valid track combination for signals, (ie, no overlap) */
 		TrackBits trackbits = GetTrackBits(tile);
-		if (KILL_FIRST_BIT(trackbits) != 0 && /* More than one track present */
+		if (KillFirstBit(trackbits) != TRACK_BIT_NONE && /* More than one track present */
 				trackbits != TRACK_BIT_HORZ &&
 				trackbits != TRACK_BIT_VERT) {
-			return CMD_ERROR;
+			return_cmd_error(STR_1005_NO_SUITABLE_RAILROAD_TRACK);
 		}
 	}
 
+	/* you can not convert a signal if no signal is on track */
+	if (convert_signal && !HasSignalOnTrack(tile, track)) return CMD_ERROR;
+
 	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 
 	if (!HasSignalOnTrack(tile, track)) {
@@ -722,6 +825,17 @@
 		if (p2 != 0 && sigvar != GetSignalVariant(tile, track)) {
 			/* convert signals <-> semaphores */
 			cost = CommandCost(_price.build_signals + _price.remove_signals);
+
+		} else if (convert_signal) {
+			/* convert button pressed */
+			if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) {
+				/* convert electric <-> semaphore */
+				cost = CommandCost(_price.build_signals + _price.remove_signals);
+			} else {
+				/* it is free to change signal type: normal-pre-exit-combo */
+				cost = CommandCost();
+			}
+
 		} else {
 			/* it is free to change orientation/pre-exit-combo signals */
 			cost = CommandCost();
@@ -734,7 +848,7 @@
 			SetHasSignals(tile, true);
 			SetSignalStates(tile, 0xF); // all signals are on
 			SetPresentSignals(tile, 0); // no signals built by default
-			SetSignalType(tile, track, SIGTYPE_NORMAL);
+			SetSignalType(tile, track, sigtype);
 			SetSignalVariant(tile, track, sigvar);
 		}
 
@@ -742,15 +856,28 @@
 			if (!HasSignalOnTrack(tile, track)) {
 				/* build new signals */
 				SetPresentSignals(tile, GetPresentSignals(tile) | SignalOnTrack(track));
-				SetSignalType(tile, track, SIGTYPE_NORMAL);
+				SetSignalType(tile, track, sigtype);
 				SetSignalVariant(tile, track, sigvar);
 			} else {
-				if (pre_signal) {
+				if (convert_signal) {
+					/* convert signal button pressed */
+					if (ctrl_pressed) {
+						/* toggle the pressent signal variant: SIG_ELECTRIC <-> SIG_SEMAPHORE */
+						SetSignalVariant(tile, track, (GetSignalVariant(tile, track) == SIG_ELECTRIC) ? SIG_SEMAPHORE : SIG_ELECTRIC);
+
+					} else {
+						/* convert the present signal to the chosen type and variant */
+						SetSignalType(tile, track, sigtype);
+						SetSignalVariant(tile, track, sigvar);
+					}
+
+				} else if (ctrl_pressed) {
 					/* cycle between normal -> pre -> exit -> combo -> ... */
-					SignalType type = GetSignalType(tile, track);
-
-					SetSignalType(tile, track, type == SIGTYPE_COMBO ? SIGTYPE_NORMAL : (SignalType)(type + 1));
+					sigtype = GetSignalType(tile, track);
+
+					SetSignalType(tile, track, sigtype == SIGTYPE_COMBO ? SIGTYPE_NORMAL : (SignalType)(sigtype + 1));
 				} else {
+					/* cycle the signal side: both -> left -> right -> both -> ... */
 					CycleSignalSide(tile, track);
 				}
 			}
@@ -798,7 +925,7 @@
 			if (IsDiagonalTrackdir(trackdir)) {
 				signal_ctr++;
 				/* Ensure signal_ctr even so X and Y pieces get signals */
-				CLRBIT(signal_ctr, 0);
+				ClrBit(signal_ctr, 0);
 			}
 			return true;
 
@@ -849,10 +976,10 @@
 	TileIndex start_tile = tile;
 
 	Track track = (Track)GB(p2, 0, 3);
-	bool mode = HASBIT(p2, 3);
-	bool semaphores = HASBIT(p2, 4);
-	bool remove = HASBIT(p2, 5);
-	bool autofill = HASBIT(p2, 6);
+	bool mode = HasBit(p2, 3);
+	bool semaphores = HasBit(p2, 4);
+	bool remove = HasBit(p2, 5);
+	bool autofill = HasBit(p2, 6);
 	Trackdir trackdir = TrackToTrackdir(track);
 	byte signal_density = GB(p2, 24, 8);
 
@@ -888,8 +1015,8 @@
 	}
 
 	byte signal_dir = 0;
-	if (signals & SignalAlongTrackdir(trackdir))   SETBIT(signal_dir, 0);
-	if (signals & SignalAgainstTrackdir(trackdir)) SETBIT(signal_dir, 1);
+	if (signals & SignalAlongTrackdir(trackdir))   SetBit(signal_dir, 0);
+	if (signals & SignalAgainstTrackdir(trackdir)) SetBit(signal_dir, 1);
 
 	/* signal_ctr         - amount of tiles already processed
 	 * signals_density    - patch setting to put signal on every Nth tile (double space on |, -- tracks)
@@ -909,8 +1036,8 @@
 
 			/* Pick the correct orientation for the track direction */
 			signals = 0;
-			if (HASBIT(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
-			if (HASBIT(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
+			if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
+			if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
 
 			ret = DoCommand(tile, p1, signals, flags, remove ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS);
 
@@ -936,7 +1063,7 @@
 			if (IsDiagonalTrackdir(trackdir)) {
 				signal_ctr++;
 			} else {
-				ToggleBitT(trackdir, 0);
+				ToggleBit(trackdir, 0);
 			}
 		}
 	}
@@ -1024,7 +1151,7 @@
  */
 CommandCost CmdRemoveSignalTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
-	return CmdSignalTrackHelper(tile, flags, p1, SETBIT(p2, 5)); // bit 5 is remove bit
+	return CmdSignalTrackHelper(tile, flags, p1, SetBit(p2, 5)); // bit 5 is remove bit
 }
 
 typedef CommandCost DoConvertRailProc(TileIndex tile, RailType totype, bool exec);
@@ -1074,7 +1201,7 @@
 		VehicleFromPos(tile, &tile, UpdateTrainPowerProc);
 	}
 
-	return CommandCost(_price.build_rail / 2);
+	return CommandCost(RailBuildCost(totype) / 2);
 }
 
 extern CommandCost DoConvertStationRail(TileIndex tile, RailType totype, bool exec);
@@ -1197,6 +1324,8 @@
 	switch (GetRailTileType(tile)) {
 		case RAIL_TILE_SIGNALS:
 		case RAIL_TILE_NORMAL: {
+			bool water_ground = (GetRailGroundType(tile) == RAIL_GROUND_WATER);
+
 			TrackBits tracks = GetTrackBits(tile);
 			while (tracks != TRACK_BIT_NONE) {
 				Track track = RemoveFirstTrack(&tracks);
@@ -1204,6 +1333,13 @@
 				if (CmdFailed(ret)) return CMD_ERROR;
 				cost.AddCost(ret);
 			}
+
+			if (water_ground) {
+				/* The track was removed, and left a coast tile. Now also clear the water. */
+				if (flags & DC_EXEC) DoClearSquare(tile);
+				cost.AddCost(_price.clear_water);
+			}
+
 			return cost;
 		}
 
@@ -1220,6 +1356,22 @@
 
 #include "table/track_land.h"
 
+/**
+ * Get surface height in point (x,y)
+ * On tiles with halftile foundations move (x,y) to a save point wrt. track
+ */
+static uint GetSaveSlopeZ(uint x, uint y, Track track)
+{
+	switch (track) {
+		case TRACK_UPPER: x &= ~0xF; y &= ~0xF; break;
+		case TRACK_LOWER: x |=  0xF; y |=  0xF; break;
+		case TRACK_LEFT:  x |=  0xF; y &= ~0xF; break;
+		case TRACK_RIGHT: x &= ~0xF; y |=  0xF; break;
+		default: break;
+	}
+	return GetSlopeZ(x, y);
+}
+
 static void DrawSingleSignal(TileIndex tile, Track track, byte condition, uint image, uint pos)
 {
 	bool side = (_opt.road_side != 0) && _patches.signal_side;
@@ -1254,16 +1406,13 @@
 
 	SpriteID sprite;
 
-	/* _signal_base is set by our NewGRF Action 5 loader. If it is 0 then we
-	 * just draw the standard signals, else we get the offset from _signal_base
-	 * and draw that sprite. All the signal sprites are loaded sequentially. */
-	if (_signal_base == 0 || (GetSignalType(tile, track) == SIGTYPE_NORMAL && GetSignalVariant(tile, track) == SIG_ELECTRIC)) {
+	if (GetSignalType(tile, track) == SIGTYPE_NORMAL && GetSignalVariant(tile, track) == SIG_ELECTRIC) {
 		sprite = SignalBase[side][GetSignalVariant(tile, track)][GetSignalType(tile, track)] + image + condition;
 	} else {
-		sprite = _signal_base + (GetSignalType(tile, track) - 1) * 16 + GetSignalVariant(tile, track) * 64 + image + condition;
+		sprite = SPR_SIGNALS_BASE + (GetSignalType(tile, track) - 1) * 16 + GetSignalVariant(tile, track) * 64 + image + condition;
 	}
 
-	AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSlopeZ(x,y));
+	AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track));
 }
 
 static uint32 _drawtile_track_palette;
@@ -1320,6 +1469,7 @@
 {
 	int z = ti->z;
 	if (ti->tileh & SLOPE_W) z += TILE_HEIGHT;
+	if (IsSteepSlope(ti->tileh)) z += TILE_HEIGHT;
 	AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_VERT, _drawtile_track_palette,
 		ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z);
 }
@@ -1331,6 +1481,7 @@
 {
 	int z = ti->z;
 	if (ti->tileh & SLOPE_E) z += TILE_HEIGHT;
+	if (IsSteepSlope(ti->tileh)) z += TILE_HEIGHT;
 	AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_VERT, _drawtile_track_palette,
 		ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z);
 }
@@ -1342,6 +1493,7 @@
 {
 	int z = ti->z;
 	if (ti->tileh & SLOPE_N) z += TILE_HEIGHT;
+	if (IsSteepSlope(ti->tileh)) z += TILE_HEIGHT;
 	AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_HORZ, _drawtile_track_palette,
 		ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z);
 }
@@ -1353,6 +1505,7 @@
 {
 	int z = ti->z;
 	if (ti->tileh & SLOPE_S) z += TILE_HEIGHT;
+	if (IsSteepSlope(ti->tileh)) z += TILE_HEIGHT;
 	AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_HORZ, _drawtile_track_palette,
 		ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z);
 }
@@ -1371,6 +1524,15 @@
 		case RAIL_GROUND_FENCE_VERT2:  DrawTrackFence_NS_2(ti);  break;
 		case RAIL_GROUND_FENCE_HORIZ1: DrawTrackFence_WE_1(ti);  break;
 		case RAIL_GROUND_FENCE_HORIZ2: DrawTrackFence_WE_2(ti);  break;
+		case RAIL_GROUND_WATER:
+			switch (GetHalftileSlopeCorner(ti->tileh)) {
+				case CORNER_W: DrawTrackFence_NS_1(ti); break;
+				case CORNER_S: DrawTrackFence_WE_2(ti); break;
+				case CORNER_E: DrawTrackFence_NS_2(ti); break;
+				case CORNER_N: DrawTrackFence_WE_1(ti); break;
+				default: NOT_REACHED();
+			}
+			break;
 		default: break;
 	}
 }
@@ -1384,41 +1546,69 @@
 static void DrawTrackBits(TileInfo* ti, TrackBits track)
 {
 	const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+	RailGroundType rgt = GetRailGroundType(ti->tile);
+	Foundation f = GetRailFoundation(ti->tileh, track);
+	Corner halftile_corner = CORNER_INVALID;
+
+	if (IsNonContinuousFoundation(f)) {
+		/* Save halftile corner */
+		halftile_corner = (f == FOUNDATION_STEEP_BOTH ? GetHighestSlopeCorner(ti->tileh) : GetHalftileFoundationCorner(f));
+		/* Draw lower part first */
+		track &= ~CornerToTrackBits(halftile_corner);
+		f = (f == FOUNDATION_STEEP_BOTH ? FOUNDATION_STEEP_LOWER : FOUNDATION_NONE);
+	}
+
+	DrawFoundation(ti, f);
+	/* DrawFoundation modifies ti */
+
 	SpriteID image;
 	SpriteID pal = PAL_NONE;
 	bool junction = false;
 
 	/* Select the sprite to use. */
-	(image = rti->base_sprites.track_y, track == TRACK_BIT_Y) ||
-	(image++,                           track == TRACK_BIT_X) ||
-	(image++,                           track == TRACK_BIT_UPPER) ||
-	(image++,                           track == TRACK_BIT_LOWER) ||
-	(image++,                           track == TRACK_BIT_RIGHT) ||
-	(image++,                           track == TRACK_BIT_LEFT) ||
-	(image++,                           track == TRACK_BIT_CROSS) ||
-
-	(image = rti->base_sprites.track_ns, track == TRACK_BIT_HORZ) ||
-	(image++,                            track == TRACK_BIT_VERT) ||
-
-	(junction = true, false) ||
-	(image = rti->base_sprites.ground, (track & TRACK_BIT_3WAY_NE) == 0) ||
-	(image++,                          (track & TRACK_BIT_3WAY_SW) == 0) ||
-	(image++,                          (track & TRACK_BIT_3WAY_NW) == 0) ||
-	(image++,                          (track & TRACK_BIT_3WAY_SE) == 0) ||
-	(image++, true);
-
-	if (ti->tileh != SLOPE_FLAT) {
-		DrawFoundation(ti, GetRailFoundation(ti->tileh, track));
-
-		/* DrawFoundation() modifies it.
-		 * Default sloped sprites.. */
-		if (ti->tileh != SLOPE_FLAT) image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y;
-	}
-
-	switch (GetRailGroundType(ti->tile)) {
-		case RAIL_GROUND_BARREN:     pal = PALETTE_TO_BARE_LAND; break;
-		case RAIL_GROUND_ICE_DESERT: image += rti->snow_offset; break;
-		default: break;
+	if (track == 0) {
+		/* Clear ground (only track on halftile foundation) */
+		if (rgt == RAIL_GROUND_WATER) {
+			image = SPR_FLAT_WATER_TILE;
+		} else {
+			switch (rgt) {
+				case RAIL_GROUND_BARREN:     image = SPR_FLAT_BARE_LAND;  break;
+				case RAIL_GROUND_ICE_DESERT: image = SPR_FLAT_SNOWY_TILE; break;
+				default:                     image = SPR_FLAT_GRASS_TILE; break;
+			}
+			image += _tileh_to_sprite[ti->tileh];
+		}
+	} else {
+		if (ti->tileh != SLOPE_FLAT) {
+			/* track on non-flat ground */
+			image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y;
+		} else {
+			/* track on flat ground */
+			(image = rti->base_sprites.track_y, track == TRACK_BIT_Y) ||
+			(image++,                           track == TRACK_BIT_X) ||
+			(image++,                           track == TRACK_BIT_UPPER) ||
+			(image++,                           track == TRACK_BIT_LOWER) ||
+			(image++,                           track == TRACK_BIT_RIGHT) ||
+			(image++,                           track == TRACK_BIT_LEFT) ||
+			(image++,                           track == TRACK_BIT_CROSS) ||
+
+			(image = rti->base_sprites.track_ns, track == TRACK_BIT_HORZ) ||
+			(image++,                            track == TRACK_BIT_VERT) ||
+
+			(junction = true, false) ||
+			(image = rti->base_sprites.ground, (track & TRACK_BIT_3WAY_NE) == 0) ||
+			(image++,                          (track & TRACK_BIT_3WAY_SW) == 0) ||
+			(image++,                          (track & TRACK_BIT_3WAY_NW) == 0) ||
+			(image++,                          (track & TRACK_BIT_3WAY_SE) == 0) ||
+			(image++, true);
+		}
+
+		switch (rgt) {
+			case RAIL_GROUND_BARREN:     pal = PALETTE_TO_BARE_LAND; break;
+			case RAIL_GROUND_ICE_DESERT: image += rti->snow_offset;  break;
+			case RAIL_GROUND_WATER:      NOT_REACHED();
+			default: break;
+		}
 	}
 
 	DrawGroundSprite(image, pal);
@@ -1432,6 +1622,30 @@
 		if (track & TRACK_BIT_LEFT)  DrawGroundSprite(rti->base_sprites.single_w, PAL_NONE);
 		if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE);
 	}
+
+	if (IsValidCorner(halftile_corner)) {
+		DrawFoundation(ti, HalftileFoundation(halftile_corner));
+
+		/* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
+		Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
+		image = _track_sloped_sprites[fake_slope - 1] + rti->base_sprites.track_y;
+		pal = PAL_NONE;
+		switch (rgt) {
+			case RAIL_GROUND_BARREN:     pal = PALETTE_TO_BARE_LAND; break;
+			case RAIL_GROUND_ICE_DESERT: image += rti->snow_offset;  break;
+			default: break;
+		}
+
+		static const int INF = 1000; // big number compared to tilesprite size
+		static const SubSprite _halftile_sub_sprite[4] = {
+			{ -INF    , -INF  , 32 - 33, INF     }, // CORNER_W, clip 33 pixels from right
+			{ -INF    ,  0 + 7, INF    , INF     }, // CORNER_S, clip 7 pixels from top
+			{ -31 + 33, -INF  , INF    , INF     }, // CORNER_E, clip 33 pixels from left
+			{ -INF    , -INF  , INF    , 30 - 23 }  // CORNER_N, clip 23 pixels from bottom
+		};
+
+		DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner]));
+	}
 }
 
 static void DrawSignals(TileIndex tile, TrackBits rails)
@@ -1478,7 +1692,7 @@
 
 		DrawTrackBits(ti, rails);
 
-		if (HASBIT(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti);
+		if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti);
 
 		if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti);
 
@@ -1518,7 +1732,7 @@
 				const Station* st = ComposeWaypointStation(ti->tile);
 				uint gfx = 2;
 
-				if (HASBIT(statspec->callbackmask, CBM_STATION_SPRITE_LAYOUT)) {
+				if (HasBit(statspec->callbackmask, CBM_STATION_SPRITE_LAYOUT)) {
 					uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile);
 					if (callback != CALLBACK_FAILED) gfx = callback;
 				}
@@ -1533,7 +1747,7 @@
 					relocation = GetCustomStationRelocation(statspec, st, ti->tile);
 
 					image = dts->ground_sprite;
-					if (HASBIT(image, SPRITE_MODIFIER_USE_OFFSET)) {
+					if (HasBit(image, SPRITE_MODIFIER_USE_OFFSET)) {
 						image += GetCustomStationGroundRelocation(statspec, st, ti->tile);
 						image += rti->custom_ground_offset;
 					} else {
@@ -1563,13 +1777,13 @@
 			/* Unlike stations, our default waypoint has no variation for
 			 * different railtype, so don't use the railtype offset if
 			 * no relocation is set */
-			if (HASBIT(image, SPRITE_MODIFIER_USE_OFFSET)) {
+			if (HasBit(image, SPRITE_MODIFIER_USE_OFFSET)) {
 				image += rti->total_offset;
 			} else {
 				image += relocation;
 			}
 
-			if (!HASBIT(_transparent_opt, TO_BUILDINGS) && HASBIT(image, PALETTE_MODIFIER_COLOR)) {
+			if (!IsTransparencySet(TO_BUILDINGS) && HasBit(image, PALETTE_MODIFIER_COLOR)) {
 				pal = _drawtile_track_palette;
 			} else {
 				pal = dtss->pal;
@@ -1581,7 +1795,7 @@
 					ti->x + dtss->delta_x, ti->y + dtss->delta_y,
 					dtss->size_x, dtss->size_y,
 					dtss->size_z, ti->z + dtss->delta_z,
-					HASBIT(_transparent_opt, TO_BUILDINGS)
+					IsTransparencySet(TO_BUILDINGS)
 				);
 			} else {
 				AddChildSpriteScreen(image, pal, dtss->delta_x, dtss->delta_y);
@@ -1601,7 +1815,7 @@
 		Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z);
 		SpriteID image = dtss->image + offset;
 
-		DrawSprite(image, HASBIT(image, PALETTE_MODIFIER_COLOR) ? palette : PAL_NONE, x + pt.x, y + pt.y);
+		DrawSprite(image, HasBit(image, PALETTE_MODIFIER_COLOR) ? palette : PAL_NONE, x + pt.x, y + pt.y);
 	}
 }
 
@@ -1917,6 +2131,11 @@
 	RailGroundType old_ground = GetRailGroundType(tile);
 	RailGroundType new_ground;
 
+	if (old_ground == RAIL_GROUND_WATER) {
+		TileLoop_Water(tile);
+		return;
+	}
+
 	switch (_opt.landscape) {
 		case LT_ARCTIC:
 			if (GetTileZ(tile) > GetSnowLine()) {
@@ -2216,26 +2435,29 @@
 	z_old += ApplyFoundationToSlope(GetRailFoundation(tileh_old, rail_bits), &tileh_old);
 	z_new += ApplyFoundationToSlope(GetRailFoundation(tileh_new, rail_bits), &tileh_new);
 
-	Slope track_corner;
+	Corner track_corner;
 	switch (rail_bits) {
-		case TRACK_BIT_LEFT:  track_corner = SLOPE_W; break;
-		case TRACK_BIT_LOWER: track_corner = SLOPE_S; break;
-		case TRACK_BIT_RIGHT: track_corner = SLOPE_E; break;
-		case TRACK_BIT_UPPER: track_corner = SLOPE_N; break;
+		case TRACK_BIT_LEFT:  track_corner = CORNER_W; break;
+		case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
+		case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
+		case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
 
 		/* Surface slope must not be changed */
 		default: return (((z_old != z_new) || (tileh_old != tileh_new)) ? CMD_ERROR : _price.terraform);
 	}
 
 	/* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
-	if ((tileh_old & track_corner) != 0) z_old += TILE_HEIGHT;
-	if ((tileh_new & track_corner) != 0) z_new += TILE_HEIGHT;
+	z_old += GetSlopeZInCorner((Slope)(tileh_old & ~SLOPE_HALFTILE_MASK), track_corner);
+	z_new += GetSlopeZInCorner((Slope)(tileh_new & ~SLOPE_HALFTILE_MASK), track_corner);
 	if (z_old != z_new) return CMD_ERROR;
 
+	CommandCost cost = CommandCost(_price.terraform);
 	/* Make the ground dirty, if surface slope has changed */
-	if ((tileh_old != tileh_new) && ((flags & DC_EXEC) != 0)) SetRailGroundType(tile, RAIL_GROUND_BARREN);
-
-	return _price.terraform;
+	if (tileh_old != tileh_new) {
+		if (GetRailGroundType(tile) == RAIL_GROUND_WATER) cost.AddCost(_price.clear_water);
+		if ((flags & DC_EXEC) != 0) SetRailGroundType(tile, RAIL_GROUND_BARREN);
+	}
+	return  cost;
 }
 
 static CommandCost TerraformTile_Track(TileIndex tile, uint32 flags, uint z_new, Slope tileh_new)
@@ -2244,6 +2466,7 @@
 	Slope tileh_old = GetTileSlope(tile, &z_old);
 	if (IsPlainRailTile(tile)) {
 		TrackBits rail_bits = GetTrackBits(tile);
+		bool was_water = GetRailGroundType(tile) == RAIL_GROUND_WATER;
 
 		_error_message = STR_1008_MUST_REMOVE_RAILROAD_TRACK;
 
@@ -2251,59 +2474,31 @@
 		CommandCost autoslope_result = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, rail_bits);
 
 		/* When there is only a single horizontal/vertical track, one corner can be terraformed. */
-		Slope allowed_corner;
+		Corner allowed_corner;
 		switch (rail_bits) {
-			case TRACK_BIT_RIGHT: allowed_corner = SLOPE_W; break;
-			case TRACK_BIT_UPPER: allowed_corner = SLOPE_S; break;
-			case TRACK_BIT_LEFT:  allowed_corner = SLOPE_E; break;
-			case TRACK_BIT_LOWER: allowed_corner = SLOPE_N; break;
+			case TRACK_BIT_RIGHT: allowed_corner = CORNER_W; break;
+			case TRACK_BIT_UPPER: allowed_corner = CORNER_S; break;
+			case TRACK_BIT_LEFT:  allowed_corner = CORNER_E; break;
+			case TRACK_BIT_LOWER: allowed_corner = CORNER_N; break;
 			default: return autoslope_result;
 		}
 
-		Slope track_corners = ComplementSlope(allowed_corner);
-
 		Foundation f_old = GetRailFoundation(tileh_old, rail_bits);
-		switch (f_old) {
-			case FOUNDATION_NONE:
-				/* Everything is valid, which only changes allowed_corner */
-
-				/* Compute height of track */
-				if (tileh_old == track_corners) z_old += TILE_HEIGHT;
-				if (tileh_new == track_corners) {
-					z_new += TILE_HEIGHT;
-				} else {
-					/* do not build a foundation */
-					if ((tileh_new != SLOPE_FLAT) && (tileh_new != allowed_corner)) return autoslope_result;
-				}
-
-				/* Track height must remain unchanged */
-				if (z_old != z_new) return autoslope_result;
-				break;
-
-			case FOUNDATION_LEVELED:
-				/* Is allowed_corner covered by the foundation? */
-				if ((tileh_old & allowed_corner) == 0) return autoslope_result;
-
-				/* allowed_corner may only be raised -> steep slope */
-				if ((z_old != z_new) || (tileh_new != (tileh_old | SLOPE_STEEP))) return autoslope_result;
-				break;
-
-			case FOUNDATION_STEEP_LOWER:
-				/* Only allow to lower highest corner */
-				if ((z_old != z_new) || (tileh_new != (tileh_old & ~SLOPE_STEEP))) return autoslope_result;
-				break;
-
-			case FOUNDATION_STEEP_HIGHER:
-				return autoslope_result;
-
-			default: NOT_REACHED();
+
+		/* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */
+		if (tileh_old != SLOPE_NS && tileh_old != SLOPE_EW && IsSpecialRailFoundation(f_old)) return autoslope_result;
+
+		/* Everything is valid, which only changes allowed_corner */
+		for (Corner corner = (Corner)0; corner < CORNER_END; corner = (Corner)(corner + 1)) {
+			if (allowed_corner == corner) continue;
+			if (z_old + GetSlopeZInCorner(tileh_old, corner) != z_new + GetSlopeZInCorner(tileh_new, corner)) return autoslope_result;
 		}
 
 		/* Make the ground dirty */
 		if ((flags & DC_EXEC) != 0) SetRailGroundType(tile, RAIL_GROUND_BARREN);
 
-		/* allow terraforming, no extra costs */
-		return CommandCost();
+		/* allow terraforming */
+		return (was_water ? CommandCost(_price.clear_water) : CommandCost());
 	} else {
 		if (_patches.build_on_slopes && AutoslopeEnabled()) {
 			switch (GetRailTileType(tile)) {