|
@@ -1304,54 +1304,58 @@ static uint RoadFindPathToStop(const Veh
|
|
|
dist = YapfRoadVehDistanceToTile(v, tile);
|
|
|
} else {
|
|
|
/* use NPF */
|
|
|
NPFFindStationOrTileData fstd;
|
|
|
Trackdir trackdir = GetVehicleTrackdir(v);
|
|
|
assert(trackdir != INVALID_TRACKDIR);
|
|
|
|
|
|
fstd.dest_coords = tile;
|
|
|
fstd.station_index = INVALID_STATION; // indicates that the destination is a tile, not a station
|
|
|
|
|
|
dist = NPFRouteToStationOrTile(v->tile, trackdir, &fstd, TRANSPORT_ROAD, v->u.road.compatible_roadtypes, v->owner, INVALID_RAILTYPE).best_path_dist;
|
|
|
/* change units from NPF_TILE_LENGTH to # of tiles */
|
|
|
if (dist != UINT_MAX)
|
|
|
dist = (dist + NPF_TILE_LENGTH - 1) / NPF_TILE_LENGTH;
|
|
|
}
|
|
|
return dist;
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
};
|
|
|
|
|
|
#include "table/roadveh.h"
|
|
|
|
|
|
static const byte _road_veh_data_1[] = {
|
|
|
20, 20, 16, 16, 0, 0, 0, 0,
|
|
|
19, 19, 15, 15, 0, 0, 0, 0,
|
|
|
16, 16, 12, 12, 0, 0, 0, 0,
|
|
|
15, 15, 11, 11
|
|
|
};
|
|
|
|
|
|
static bool RoadVehLeaveDepot(Vehicle *v, bool first)
|
|
|
{
|
|
|
/* Don't leave if not all the wagons are in the depot. */
|
|
|
for (const Vehicle *u = v; u != NULL; u = u->Next()) {
|
|
|
if (u->u.road.state != RVSB_IN_DEPOT || u->tile != v->tile) return false;
|
|
|
}
|
|
|
|
|
|
DiagDirection dir = GetRoadDepotDirection(v->tile);
|
|
|
v->direction = DiagDirToDir(dir);
|
|
@@ -1435,48 +1439,71 @@ static Trackdir FollowPreviousRoadVehicl
|
|
|
{ TRACKDIR_UPPER_W, TRACKDIR_RIGHT_N, TRACKDIR_LEFT_N, TRACKDIR_UPPER_E },
|
|
|
{ TRACKDIR_RIGHT_S, TRACKDIR_LOWER_W, TRACKDIR_LOWER_E, TRACKDIR_LEFT_S }};
|
|
|
dir = reversed_turn_lookup[prev->tile < tile ? 0 : 1][ReverseDiagDir(entry_dir)];
|
|
|
} else {
|
|
|
dir = (Trackdir)prev_state;
|
|
|
}
|
|
|
} else {
|
|
|
return INVALID_TRACKDIR;
|
|
|
}
|
|
|
|
|
|
/* Do some sanity checking. */
|
|
|
static const RoadBits required_roadbits[] = {
|
|
|
ROAD_X, ROAD_Y, ROAD_NW | ROAD_NE, ROAD_SW | ROAD_SE,
|
|
|
ROAD_NW | ROAD_SW, ROAD_NE | ROAD_SE, ROAD_X, ROAD_Y
|
|
|
};
|
|
|
RoadBits required = required_roadbits[dir & 0x07];
|
|
|
|
|
|
if ((required & GetAnyRoadBits(tile, v->u.road.roadtype)) == ROAD_NONE) {
|
|
|
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;
|
|
|
uint32 r;
|
|
|
|
|
|
if (v->u.road.overtaking != 0) {
|
|
|
if (++v->u.road.overtaking_ctr >= 35)
|
|
|
/* If overtaking just aborts at a random moment, we can have a out-of-bound problem,
|
|
|
* if the vehicle started a corner. To protect that, only allow an abort of
|
|
|
* overtake if we are on straight roads */
|
|
|
if (v->u.road.state < RVSB_IN_ROAD_STOP && IsStraightRoadTrackdir((Trackdir)v->u.road.state)) {
|
|
|
v->u.road.overtaking = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* If this vehicle is in a depot and we've reached this point it must be
|
|
|
* one of the articulated parts. It will stay in the depot until activated
|
|
|
* by the previous vehicle in the chain when it gets to the right place. */
|
|
|
if (v->IsInDepot()) return true;
|
|
|
|
|
|
/* Save old vehicle position to use at end of move to set viewport area dirty */
|
|
@@ -1514,163 +1541,217 @@ static bool IndividualRoadVehicleControl
|
|
|
(HASBIT(v->u.road.state, RVS_IN_DT_ROAD_STOP) ? v->u.road.state & RVSB_ROAD_STOP_TRACKDIR_MASK : v->u.road.state) +
|
|
|
(_opt.road_side << RVS_DRIVE_SIDE)) ^ v->u.road.overtaking][v->u.road.frame + 1];
|
|
|
|
|
|
if (rd.x & RDE_NEXT_TILE) {
|
|
|
TileIndex tile = v->tile + TileOffsByDiagDir((DiagDirection)(rd.x & 3));
|
|
|
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, tile, (DiagDirection)(rd.x & 3));
|
|
|
} else {
|
|
|
dir = FollowPreviousRoadVehicle(v, prev, tile, (DiagDirection)(rd.x & 3), false);
|
|
|
}
|
|
|
|
|
|
if (dir == INVALID_TRACKDIR) {
|
|
|
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;
|
|
|
} else {
|
|
|
tile = v->tile;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* 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)) {
|
|
|
if (!IsTileType(tile, MP_TUNNELBRIDGE)) {
|
|
|
v->cur_speed = 0;
|
|
|
return false;
|
|
|
}
|
|
|
/* Try an about turn to re-enter the previous tile */
|
|
|
dir = _road_reverse_table[rd.x & 3];
|
|
|
goto again;
|
|
|
}
|
|
|
|
|
|
if (IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) && IsTileType(v->tile, MP_STATION)) {
|
|
|
if (IsReversingRoadTrackdir(dir) && IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) {
|
|
|
/* New direction is trying to turn vehicle around.
|
|
|
* We can't turn at the exit of a road stop so wait.*/
|
|
|
v->cur_speed = 0;
|
|
|
return false;
|
|
|
}
|
|
|
if (IsRoadStop(v->tile)) {
|
|
|
RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
|
|
|
|
|
|
/* Vehicle is leaving a road stop tile, mark bay as free
|
|
|
* For drive-through stops, only do it if the vehicle stopped here */
|
|
|
if (IsStandardRoadStopTile(v->tile) || HASBIT(v->u.road.state, RVS_IS_STOPPING)) {
|
|
|
rs->FreeBay(HASBIT(v->u.road.state, RVS_USING_SECOND_BAY));
|
|
|
CLRBIT(v->u.road.state, RVS_IS_STOPPING);
|
|
|
}
|
|
|
if (IsStandardRoadStopTile(v->tile)) rs->SetEntranceBusy(false);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
v->cur_image = v->GetImage(newdir);
|
|
|
v->UpdateDeltaXY(v->direction);
|
|
|
RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y));
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
if (rd.x & RDE_TURNED) {
|
|
|
/* 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;
|
|
|
}
|
|
|
|
|
|
v->cur_image = v->GetImage(newdir);
|
|
|
v->UpdateDeltaXY(v->direction);
|
|
|
RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y));
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/* This vehicle is not in a wormhole and it hasn't entered a new tile. If
|
|
|
* it's on a depot tile, check if it's time to activate the next vehicle in
|
|
|
* the chain yet. */
|
|
|
if (v->Next() != NULL &&
|
|
|
IsTileType(v->tile, MP_ROAD) && GetRoadTileType(v->tile) == ROAD_TILE_DEPOT) {
|
|
|
|
|
|
if (v->u.road.frame == v->u.road.cached_veh_length + RVC_DEPOT_START_FRAME) {
|
|
|
RoadVehLeaveDepot(v->Next(), false);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Calculate new position for the vehicle */
|