summaryrefslogtreecommitdiff
path: root/src/roadveh_cmd.cpp
diff options
context:
space:
mode:
authorrubidium <rubidium@openttd.org>2007-11-17 12:42:15 +0000
committerrubidium <rubidium@openttd.org>2007-11-17 12:42:15 +0000
commitef6c4c1cdfad2614fa24d96fc26af6b7f57f4d23 (patch)
tree841decf2bd4b1149856cf8d6e0d1ac9deb6c5e5e /src/roadveh_cmd.cpp
parentfd9e2887b2450fc75992a3df9233ae47862ce17d (diff)
downloadopenttd-ef6c4c1cdfad2614fa24d96fc26af6b7f57f4d23.tar.xz
(svn r11449) -Fix [FS#1160]: trams could deadlock themselves. As of now trams will turn as roadvehicles do when the player cannot build a tram track piece on the next tile without destroying anything. It will not turn when the player can build the before mentioned track piece on the 'next' tile.
Diffstat (limited to 'src/roadveh_cmd.cpp')
-rw-r--r--src/roadveh_cmd.cpp117
1 files changed, 99 insertions, 18 deletions
diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp
index 8bfbaa764..0927c69a9 100644
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -1325,12 +1325,16 @@ enum {
/* Start frames for when a vehicle enters a tile/changes its state.
* The start frame is different for vehicles that turned around or
- * are leaving the depot as the do not start at the edge of the tile */
- RVC_DEFAULT_START_FRAME = 0,
- RVC_TURN_AROUND_START_FRAME = 1,
- RVC_DEPOT_START_FRAME = 6,
+ * are leaving the depot as the do not start at the edge of the tile.
+ * For trams there are a few different start frames as there are two
+ * places where trams can turn. */
+ RVC_DEFAULT_START_FRAME = 0,
+ RVC_TURN_AROUND_START_FRAME = 1,
+ RVC_DEPOT_START_FRAME = 6,
+ RVC_START_FRAME_AFTER_LONG_TRAM = 22,
+ RVC_TURN_AROUND_START_FRAME_SHORT_TRAM = 16,
/* Stop frame for a vehicle in a drive-through stop */
- RVC_DRIVE_THROUGH_STOP_FRAME = 7
+ RVC_DRIVE_THROUGH_STOP_FRAME = 7
};
struct RoadDriveEntry {
@@ -1456,6 +1460,29 @@ static Trackdir FollowPreviousRoadVehicle(const Vehicle *v, const Vehicle *prev,
return dir;
}
+/**
+ * Can a tram track build without destruction on the given tile?
+ * @param t the tile to build on.
+ * @return true when a track track can be build on 't'
+ */
+static bool CanBuildTramTrackOnTile(TileIndex t)
+{
+ switch (GetTileType(t)) {
+ case MP_CLEAR:
+ case MP_TREES:
+ return true;
+
+ case MP_ROAD:
+ return GetRoadTileType(t) == ROAD_TILE_NORMAL;
+
+ case MP_WATER:
+ return IsCoast(t);
+
+ default:
+ return false;
+ }
+}
+
static bool IndividualRoadVehicleController(Vehicle *v, const Vehicle *prev)
{
Direction new_dir;
@@ -1535,10 +1562,13 @@ static bool IndividualRoadVehicleController(Vehicle *v, const Vehicle *prev)
}
again:
+ uint start_frame = RVC_DEFAULT_START_FRAME;
if (IsReversingRoadTrackdir(dir)) {
/* Turning around */
if (v->u.road.roadtype == ROADTYPE_TRAM) {
- RoadBits needed; // The road bits the tram needs to be able to turn around
+ /* Determine the road bits the tram needs to be able to turn around
+ * using the 'big' corner loop. */
+ RoadBits needed;
switch (dir) {
default: NOT_REACHED();
case TRACKDIR_RVREV_NE: needed = ROAD_SW; break;
@@ -1546,8 +1576,36 @@ again:
case TRACKDIR_RVREV_SW: needed = ROAD_NE; break;
case TRACKDIR_RVREV_NW: needed = ROAD_SE; break;
}
- if (!IsTileType(tile, MP_ROAD) || GetRoadTileType(tile) != ROAD_TILE_NORMAL || HasRoadWorks(tile) || (needed & GetRoadBits(tile, ROADTYPE_TRAM)) == ROAD_NONE) {
- /* The tram cannot turn here */
+ if ((v->Previous() != NULL && v->Previous()->tile == tile) ||
+ (IsRoadVehFront(v) && IsTileType(tile, MP_ROAD) &&
+ GetRoadTileType(tile) == ROAD_TILE_NORMAL && !HasRoadWorks(tile) &&
+ (needed & GetRoadBits(tile, ROADTYPE_TRAM)) != ROAD_NONE)) {
+ /*
+ * Taking the 'big' corner for trams only happens when:
+ * - The previous vehicle in this (articulated) tram chain is
+ * already on the 'next' tile, we just follow them regardless of
+ * anything. When it is NOT on the 'next' tile, the tram started
+ * doing a reversing turn when the piece of tram track on the next
+ * tile did not exist yet. Do not use the big tram loop as that is
+ * going to cause the tram to split up.
+ * - Or the front of the tram can drive over the next tile.
+ */
+ } else if (!IsRoadVehFront(v) || !CanBuildTramTrackOnTile(tile)) {
+ /*
+ * Taking the 'small' corner for trams only happens when:
+ * - We are not the from vehicle of an articulated tram.
+ * - Or when the player cannot build on the next tile.
+ *
+ * The 'small' corner means that the vehicle is on the end of a
+ * tram track and needs to start turning there. To do this properly
+ * the tram needs to start at an offset in the tram turning 'code'
+ * for 'big' corners. It furthermore does not go to the next tile,
+ * so that needs to be fixed too.
+ */
+ tile = v->tile;
+ start_frame = RVC_TURN_AROUND_START_FRAME_SHORT_TRAM;
+ } else {
+ /* The player can build on the next tile, so wait till (s)he does. */
v->cur_speed = 0;
return false;
}
@@ -1562,8 +1620,8 @@ again:
/* Get position data for first frame on the new tile */
rdp = _road_drive_data[v->u.road.roadtype][(dir + (_opt.road_side << RVS_DRIVE_SIDE)) ^ v->u.road.overtaking];
- x = TileX(tile) * TILE_SIZE + rdp[RVC_DEFAULT_START_FRAME].x;
- y = TileY(tile) * TILE_SIZE + rdp[RVC_DEFAULT_START_FRAME].y;
+ x = TileX(tile) * TILE_SIZE + rdp[start_frame].x;
+ y = TileY(tile) * TILE_SIZE + rdp[start_frame].y;
newdir = RoadVehGetSlidingDirection(v, x, y);
if (IsRoadVehFront(v) && RoadVehFindCloseTo(v, x, y, newdir) != NULL) return false;
@@ -1602,7 +1660,7 @@ again:
if (!HASBIT(r, VETS_ENTERED_WORMHOLE)) {
v->tile = tile;
v->u.road.state = (byte)dir;
- v->u.road.frame = RVC_DEFAULT_START_FRAME;
+ v->u.road.frame = start_frame;
}
if (newdir != v->direction) {
v->direction = newdir;
@@ -1622,11 +1680,34 @@ again:
Direction newdir;
const RoadDriveEntry *rdp;
- if (IsRoadVehFront(v)) {
- /* If this is the front engine, look for the right path. */
- dir = RoadFindPathToDest(v, v->tile, (DiagDirection)(rd.x & 3));
+ uint turn_around_start_frame = RVC_TURN_AROUND_START_FRAME;
+
+ RoadBits tram = GetRoadBits(v->tile, ROADTYPE_TRAM);
+ if (v->u.road.roadtype == ROADTYPE_TRAM && CountBits(tram) == 1) {
+ /*
+ * The tram is turning around with one tram 'roadbit'. This means that
+ * it is using the 'big' corner 'drive data'. However, to support the
+ * trams to take a small corner, there is a 'turned' marker in the middle
+ * of the turning 'drive data'. When the tram took the long corner, we
+ * will still use the 'big' corner drive data, but we advance it one
+ * frame. We furthermore set the driving direction so the turning is
+ * going to be properly shown.
+ */
+ turn_around_start_frame = RVC_START_FRAME_AFTER_LONG_TRAM;
+ switch (tram) {
+ default: NOT_REACHED();
+ case ROAD_SW: dir = TRACKDIR_RVREV_NE; break;
+ case ROAD_NW: dir = TRACKDIR_RVREV_SE; break;
+ case ROAD_NE: dir = TRACKDIR_RVREV_SW; break;
+ case ROAD_SE: dir = TRACKDIR_RVREV_NW; break;
+ }
} else {
- dir = FollowPreviousRoadVehicle(v, prev, v->tile, (DiagDirection)(rd.x & 3), true);
+ if (IsRoadVehFront(v)) {
+ /* If this is the front engine, look for the right path. */
+ dir = RoadFindPathToDest(v, v->tile, (DiagDirection)(rd.x & 3));
+ } else {
+ dir = FollowPreviousRoadVehicle(v, prev, v->tile, (DiagDirection)(rd.x & 3), true);
+ }
}
if (dir == INVALID_TRACKDIR) {
@@ -1636,8 +1717,8 @@ again:
rdp = _road_drive_data[v->u.road.roadtype][(_opt.road_side << RVS_DRIVE_SIDE) + dir];
- x = TileX(v->tile) * TILE_SIZE + rdp[RVC_TURN_AROUND_START_FRAME].x;
- y = TileY(v->tile) * TILE_SIZE + rdp[RVC_TURN_AROUND_START_FRAME].y;
+ x = TileX(v->tile) * TILE_SIZE + rdp[turn_around_start_frame].x;
+ y = TileY(v->tile) * TILE_SIZE + rdp[turn_around_start_frame].y;
newdir = RoadVehGetSlidingDirection(v, x, y);
if (IsRoadVehFront(v) && RoadVehFindCloseTo(v, x, y, newdir) != NULL) return false;
@@ -1649,7 +1730,7 @@ again:
}
v->u.road.state = dir;
- v->u.road.frame = RVC_TURN_AROUND_START_FRAME;
+ v->u.road.frame = turn_around_start_frame;
if (newdir != v->direction) {
v->direction = newdir;