|
@@ -1322,18 +1322,22 @@ static uint RoadFindPathToStop(const Veh
|
|
|
enum {
|
|
|
RDE_NEXT_TILE = 0x80,
|
|
|
RDE_TURNED = 0x40,
|
|
|
|
|
|
/* 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 {
|
|
|
byte x, y;
|
|
|
};
|
|
|
|
|
@@ -1453,12 +1457,35 @@ static Trackdir FollowPreviousRoadVehicl
|
|
|
dir = INVALID_TRACKDIR;
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
Direction old_dir;
|
|
|
RoadDriveEntry rd;
|
|
|
int x,y;
|
|
@@ -1532,25 +1559,56 @@ static bool IndividualRoadVehicleControl
|
|
|
if (!IsRoadVehFront(v)) error("!Disconnecting road vehicle.");
|
|
|
v->cur_speed = 0;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
case TRACKDIR_RVREV_SE: needed = ROAD_NW; break;
|
|
|
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;
|
|
|
}
|
|
|
} else if (IsTileType(v->tile, MP_ROAD) && GetRoadTileType(v->tile) == ROAD_TILE_NORMAL && GetDisallowedRoadDirections(v->tile) != DRD_NONE) {
|
|
|
v->cur_speed = 0;
|
|
|
return false;
|
|
@@ -1559,14 +1617,14 @@ 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;
|
|
|
|
|
|
r = VehicleEnterTile(v, tile, x, y);
|
|
|
if (HASBIT(r, VETS_CANNOT_ENTER)) {
|
|
@@ -1599,13 +1657,13 @@ 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;
|
|
|
v->cur_speed -= v->cur_speed >> 2;
|
|
|
}
|
|
|
|
|
@@ -1619,40 +1677,63 @@ again:
|
|
|
/* Vehicle has finished turning around, it will now head back onto the same tile */
|
|
|
Trackdir dir;
|
|
|
uint32 r;
|
|
|
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) {
|
|
|
v->cur_speed = 0;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
|
r = VehicleEnterTile(v, v->tile, x, y);
|
|
|
if (HASBIT(r, VETS_CANNOT_ENTER)) {
|
|
|
v->cur_speed = 0;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
v->cur_speed -= v->cur_speed >> 2;
|
|
|
}
|
|
|
|