From 224509482235b3761eed649cb992d96186c2aba2 Mon Sep 17 00:00:00 2001 From: rubidium Date: Sat, 17 Nov 2007 12:42:15 +0000 Subject: [PATCH] (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. --- src/roadveh_cmd.cpp | 117 +++++++++++++++++++++++++++++++++++++------- src/table/roadveh.h | 8 +++ 2 files changed, 107 insertions(+), 18 deletions(-) diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 8bfbaa764a..0927c69a9e 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; diff --git a/src/table/roadveh.h b/src/table/roadveh.h index 6f8c7a66d1..2ec38760a6 100644 --- a/src/table/roadveh.h +++ b/src/table/roadveh.h @@ -1099,6 +1099,7 @@ static const RoadDriveEntry _roadveh_tram_turn_ne_0[] = { { 0, 7}, { 0, 8}, { 0, 9}, + {RDE_TURNED | DIAGDIR_SW, 0}, { 1, 9}, { 2, 9}, { 3, 9}, @@ -1138,6 +1139,7 @@ static const RoadDriveEntry _roadveh_tram_turn_ne_1[] = { { 0, 7}, { 0, 6}, { 0, 5}, + {RDE_TURNED | DIAGDIR_SW, 0}, { 1, 5}, { 2, 5}, { 3, 5}, @@ -1177,6 +1179,7 @@ static const RoadDriveEntry _roadveh_tram_turn_se_0[] = { {7, 15}, {8, 15}, {9, 15}, + {RDE_TURNED | DIAGDIR_NW, 0}, {9, 14}, {9, 13}, {9, 12}, @@ -1216,6 +1219,7 @@ static const RoadDriveEntry _roadveh_tram_turn_se_1[] = { {7, 15}, {6, 15}, {5, 15}, + {RDE_TURNED | DIAGDIR_NW, 0}, {5, 14}, {5, 13}, {5, 12}, @@ -1255,6 +1259,7 @@ static const RoadDriveEntry _roadveh_tram_turn_sw_0[] = { {15, 7}, {15, 6}, {15, 5}, + {RDE_TURNED | DIAGDIR_SW, 0}, {14, 5}, {13, 5}, {12, 5}, @@ -1293,6 +1298,7 @@ static const RoadDriveEntry _roadveh_tram_turn_sw_1[] = { {15, 7}, {15, 8}, {15, 9}, + {RDE_TURNED | DIAGDIR_NE, 0}, {14, 9}, {13, 9}, {12, 9}, @@ -1332,6 +1338,7 @@ static const RoadDriveEntry _roadveh_tram_turn_nw_0[] = { {7, 0}, {6, 0}, {5, 0}, + {RDE_TURNED | DIAGDIR_SE, 0}, {5, 1}, {5, 2}, {5, 3}, @@ -1370,6 +1377,7 @@ static const RoadDriveEntry _roadveh_tram_turn_nw_1[] = { {7, 0}, {8, 0}, {9, 0}, + {RDE_TURNED | DIAGDIR_SE, 0}, {9, 1}, {9, 2}, {9, 3},