--- 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)) {