|
@@ -1316,30 +1316,34 @@ static uint RoadFindPathToStop(const Veh
|
|
|
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,
|
|
@@ -1447,24 +1451,47 @@ static Trackdir FollowPreviousRoadVehicl
|
|
|
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
|
|
@@ -1526,53 +1553,84 @@ static bool IndividualRoadVehicleControl
|
|
|
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];
|
|
@@ -1593,72 +1651,95 @@ again:
|
|
|
* 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;
|
|
|
}
|
|
|
|