Add support for overtaking inside (non-custom) bridges/tunnels

pull/203/head
Jonathan G Rennison 4 years ago
parent 5a952d6c7f
commit 57d47b9bae

@ -835,6 +835,8 @@ struct OvertakeData {
const RoadVehicle *v;
TileIndex tile;
Trackdir trackdir;
int tunnelbridge_min;
int tunnelbridge_max;
};
static Vehicle *EnumFindVehBlockingOvertake(Vehicle *v, void *data)
@ -864,6 +866,32 @@ static Vehicle *EnumFindVehBlockingOvertake(Vehicle *v, void *data)
return v;
}
static Vehicle *EnumFindVehBlockingOvertakeTunnelBridge(Vehicle *v, void *data)
{
const OvertakeData *od = (OvertakeData*)data;
switch (DiagDirToAxis(DirToDiagDir(v->direction))) {
case AXIS_X:
if (v->x_pos < od->tunnelbridge_min || v->x_pos > od->tunnelbridge_max) return nullptr;
break;
case AXIS_Y:
if (v->y_pos < od->tunnelbridge_min || v->y_pos > od->tunnelbridge_max) return nullptr;
break;
default:
NOT_REACHED();
}
return EnumFindVehBlockingOvertake(v, data);
}
static Vehicle *EnumFindVehBlockingOvertakeBehind(Vehicle *v, void *data)
{
const OvertakeData *od = (OvertakeData*)data;
if (v->First() == od->u || v->First() == od->v) return nullptr;
if (RoadVehicle::From(v)->overtaking != 0 && TileVirtXY(v->x_pos, v->y_pos) == od->tile) return v;
return nullptr;
}
static bool CheckRoadInfraUnsuitableForOvertaking(OvertakeData *od)
{
if (!HasTileAnyRoadType(od->tile, od->v->compatible_roadtypes)) return true;
@ -884,7 +912,7 @@ static bool CheckRoadInfraUnsuitableForOvertaking(OvertakeData *od)
return true;
}
} else {
return rcows != RCOWS_SIDE_JUNCTION_NO_EXIT;
return rcows == RCOWS_NORMAL || rcows == RCOWS_NO_ACCESS;
}
}
@ -924,6 +952,34 @@ inline bool IsValidRoadVehStateForOvertake(const RoadVehicle *v)
return true;
}
static bool CheckTunnelBridgeBlockedForOvertaking(OvertakeData *od, TileIndex behind_end, TileIndex ahead_end, TileIndex pos, int ahead_extent, int behind_extent)
{
switch (DirToDiagDir(od->v->direction)) {
case DIAGDIR_NE:
od->tunnelbridge_min = (TileX(pos) - ahead_extent) * TILE_SIZE;
od->tunnelbridge_max = ((TileX(pos) + behind_extent) * TILE_SIZE) + TILE_UNIT_MASK;
break;
case DIAGDIR_SE:
od->tunnelbridge_min = (TileY(pos) - behind_extent) * TILE_SIZE;
od->tunnelbridge_max = ((TileY(pos) + ahead_extent) * TILE_SIZE) + TILE_UNIT_MASK;
break;
case DIAGDIR_SW:
od->tunnelbridge_min = (TileX(pos) - behind_extent) * TILE_SIZE;
od->tunnelbridge_max = ((TileX(pos) + ahead_extent) * TILE_SIZE) + TILE_UNIT_MASK;
break;
case DIAGDIR_NW:
od->tunnelbridge_min = (TileY(pos) - ahead_extent) * TILE_SIZE;
od->tunnelbridge_max = ((TileY(pos) + behind_extent) * TILE_SIZE) + TILE_UNIT_MASK;
break;
default:
NOT_REACHED();
}
if (HasVehicleOnPos(behind_end, VEH_ROAD, od, EnumFindVehBlockingOvertakeTunnelBridge)) return true;
if (HasVehicleOnPos(ahead_end, VEH_ROAD, od, EnumFindVehBlockingOvertakeTunnelBridge)) return true;
return false;
}
static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
{
/* Trams can't overtake other trams */
@ -955,9 +1011,9 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
/* Don't overtake if vehicle parts not all in same direction */
if (w->direction != v->direction) return;
/* Check if vehicle is in a road stop, depot, tunnel or bridge or not on a straight road */
/* Check if vehicle is in a road stop, depot, or not on a straight road */
if ((w->state >= RVSB_IN_ROAD_STOP || !IsStraightRoadTrackdir((Trackdir)(w->state & RVSB_TRACKDIR_MASK))) &&
!IsInsideMM(w->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
!IsInsideMM(w->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) && w->state != RVSB_WORMHOLE) {
return;
}
}
@ -983,12 +1039,40 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
* - No barred levelcrossing
* - No other vehicles in the way
*/
uint tile_count = 1 + CeilDiv(v->gcache.cached_total_length, TILE_SIZE);
int tile_count = 1 + CeilDiv(v->gcache.cached_total_length, TILE_SIZE);
TileIndex check_tile = v->tile;
DiagDirection dir = DirToDiagDir(v->direction);
TileIndexDiff check_tile_diff = TileOffsByDiagDir(DirToDiagDir(v->direction));
for (; tile_count != 0; tile_count--, check_tile += check_tile_diff) {
TileIndex behind_check_tile = v->tile - check_tile_diff;
int tile_offset = ((DiagDirToAxis(DirToDiagDir(v->direction)) == AXIS_X) ? v->x_pos : v->y_pos) & 0xF;
int tile_ahead_margin = ((dir == DIAGDIR_SE || dir == DIAGDIR_SW) ? TILE_SIZE - 1 - tile_offset : tile_offset);;
int behind_tile_count = (v->gcache.cached_total_length + tile_ahead_margin) / TILE_SIZE;
if (IsTileType(check_tile, MP_TUNNELBRIDGE)) {
TileIndex behind_end = GetOtherTunnelBridgeEnd(check_tile);
if (IsBridgeTile(check_tile) && (IsRoadCustomBridgeHeadTile(check_tile) || IsRoadCustomBridgeHeadTile(behind_end))) return;
if (GetTunnelBridgeDirection(check_tile) == dir) std::swap(check_tile, behind_end);
TileIndex veh_tile = TileVirtXY(v->x_pos, v->y_pos);
bool one_way = GetRoadCachedOneWayState(check_tile) != RCOWS_NORMAL;
if (CheckTunnelBridgeBlockedForOvertaking(&od, behind_end, check_tile, veh_tile, one_way ? 0 : (tile_count - 1), behind_tile_count)) return;
tile_count -= DistanceManhattan(check_tile, veh_tile);
behind_tile_count -= DistanceManhattan(behind_end, veh_tile);
check_tile += check_tile_diff;
behind_check_tile = behind_end - check_tile_diff;
}
for (; tile_count > 0; tile_count--, check_tile += check_tile_diff) {
od.tile = check_tile;
if (CheckRoadInfraUnsuitableForOvertaking(&od)) return;
if (IsTileType(check_tile, MP_TUNNELBRIDGE)) {
TileIndex ahead_end = GetOtherTunnelBridgeEnd(check_tile);
if (IsBridgeTile(check_tile) && (IsRoadCustomBridgeHeadTile(check_tile) || IsRoadCustomBridgeHeadTile(ahead_end))) return;
if (GetRoadCachedOneWayState(check_tile) == RCOWS_NORMAL && CheckTunnelBridgeBlockedForOvertaking(&od, check_tile, ahead_end, check_tile, tile_count - 1, 0)) return;
tile_count -= DistanceManhattan(check_tile, ahead_end);
check_tile = ahead_end;
continue;
}
if (IsDriveThroughStopTile(check_tile) && GetDriveThroughStopDisallowedRoadDirections(check_tile) != DRD_NONE) {
const RoadStop *rs = RoadStop::GetByTile(check_tile, GetRoadStopType(check_tile));
DiagDirection dir = DirToDiagDir(v->direction);
@ -1003,18 +1087,22 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
}
if (CheckRoadBlockedForOvertaking(&od)) return;
}
DiagDirection dir = DirToDiagDir(v->direction);
int tile_offset = ((DiagDirToAxis(DirToDiagDir(v->direction)) == AXIS_X) ? v->x_pos : v->y_pos) & 0xF;
int tile_ahead_margin = ((dir == DIAGDIR_SE || dir == DIAGDIR_SW) ? TILE_SIZE - 1 - tile_offset : tile_offset);;
tile_count = (v->gcache.cached_total_length + tile_ahead_margin) / TILE_SIZE;
check_tile = v->tile - check_tile_diff;
for (; tile_count != 0; tile_count--, check_tile -= check_tile_diff) {
od.tile = check_tile;
if (tile_count == 1) {
RoadBits rb = GetAnyRoadBits(check_tile, RTT_ROAD);
if ((rb & DiagDirToRoadBits(dir)) && HasVehicleOnPos(check_tile, VEH_ROAD, &od, EnumFindVehBlockingOvertakeBehind)) return;
for (; behind_tile_count > 0; behind_tile_count--, behind_check_tile -= check_tile_diff) {
od.tile = behind_check_tile;
if (behind_tile_count == 1) {
RoadBits rb = GetAnyRoadBits(behind_check_tile, RTT_ROAD);
if ((rb & DiagDirToRoadBits(dir)) && HasVehicleOnPos(behind_check_tile, VEH_ROAD, &od, EnumFindVehBlockingOvertakeBehind)) return;
} else {
if (CheckRoadInfraUnsuitableForOvertaking(&od)) return;
if (IsTileType(behind_check_tile, MP_TUNNELBRIDGE)) {
TileIndex behind_end = GetOtherTunnelBridgeEnd(behind_check_tile);
if (IsBridgeTile(behind_check_tile) && (IsRoadCustomBridgeHeadTile(behind_check_tile) || IsRoadCustomBridgeHeadTile(behind_end))) return;
if (CheckTunnelBridgeBlockedForOvertaking(&od, behind_check_tile, behind_end, behind_check_tile, 0, behind_tile_count - 1)) return;
behind_tile_count -= DistanceManhattan(behind_check_tile, behind_end);
check_tile = behind_end;
continue;
}
if (CheckRoadBlockedForOvertaking(&od)) return;
}
}
@ -1390,7 +1478,8 @@ static void RoadVehCheckFinishOvertake(RoadVehicle *v)
const RoadVehicle *last = v->Last();
const int front_margin = 10;
const int back_margin = 10;
switch (DirToDiagDir(v->direction)) {
DiagDirection dir = DirToDiagDir(v->direction);
switch (dir) {
case DIAGDIR_NE:
od.min_coord = v->x_pos - front_margin;
od.max_coord = last->x_pos + back_margin;
@ -1416,13 +1505,43 @@ static void RoadVehCheckFinishOvertake(RoadVehicle *v)
}
TileIndexDiffC ti = TileIndexDiffCByDiagDir(DirToDiagDir(v->direction));
TileIndex ahead_tile = TileAddWrap(v->tile, ti.x, ti.y);
if (ahead_tile != INVALID_TILE && HasVehicleOnPos(ahead_tile, VEH_ROAD, &od, EnumFindVehBlockingFinishOvertake)) return;
bool check_ahead = true;
int tiles_behind = 1 + CeilDiv(v->gcache.cached_total_length, TILE_SIZE);
uint tile_count = 1 + CeilDiv(v->gcache.cached_total_length, TILE_SIZE);
TileIndex check_tile = v->tile;
for (; check_tile != INVALID_TILE && tile_count != 0; tile_count--, check_tile = TileAddWrap(check_tile, -ti.x, -ti.y)) {
if (IsTileType(check_tile, MP_TUNNELBRIDGE)) {
TileIndex ahead = GetOtherTunnelBridgeEnd(check_tile);
if (v->state == RVSB_WORMHOLE) {
check_ahead = false;
}
if (GetTunnelBridgeDirection(check_tile) == dir) {
check_ahead = false;
} else if (GetTunnelBridgeDirection(check_tile) == ReverseDiagDir(dir)) {
std::swap(ahead, check_tile);
}
if (HasVehicleOnPos(ahead, VEH_ROAD, &od, EnumFindVehBlockingFinishOvertake)) return;
if (HasVehicleOnPos(check_tile, VEH_ROAD, &od, EnumFindVehBlockingFinishOvertake)) return;
tiles_behind -= 1 + DistanceManhattan(check_tile, TileVirtXY(v->x_pos, v->y_pos));
check_tile = TileAddWrap(check_tile, -ti.x, -ti.y);
}
if (check_ahead > 0) {
TileIndex ahead_tile = TileAddWrap(check_tile, ti.x, ti.y);
if (ahead_tile != INVALID_TILE) {
if (HasVehicleOnPos(ahead_tile, VEH_ROAD, &od, EnumFindVehBlockingFinishOvertake)) return;
if (IsTileType(ahead_tile, MP_TUNNELBRIDGE) && HasVehicleOnPos(GetOtherTunnelBridgeEnd(ahead_tile), VEH_ROAD, &od, EnumFindVehBlockingFinishOvertake)) return;
}
}
for (; check_tile != INVALID_TILE && tiles_behind > 0; tiles_behind--, check_tile = TileAddWrap(check_tile, -ti.x, -ti.y)) {
if (HasVehicleOnPos(check_tile, VEH_ROAD, &od, EnumFindVehBlockingFinishOvertake)) return;
if (IsTileType(check_tile, MP_TUNNELBRIDGE)) {
TileIndex other_end = GetOtherTunnelBridgeEnd(check_tile);
tiles_behind -= DistanceManhattan(other_end, check_tile);
if (HasVehicleOnPos(other_end, VEH_ROAD, &od, EnumFindVehBlockingFinishOvertake)) return;
check_tile = other_end;
}
}
/* road on the normal side is clear, finish overtake */
@ -1438,11 +1557,11 @@ inline byte IncreaseOvertakingCounter(RoadVehicle *v)
bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
{
SCOPE_INFO_FMT([&], "IndividualRoadVehicleController: %s, %s", scope_dumper().VehicleInfo(v), scope_dumper().VehicleInfo(prev));
if (v->overtaking != 0 && v->IsFrontEngine()) {
if (v->overtaking & RVSB_DRIVE_SIDE && v->IsFrontEngine()) {
if (IsNonOvertakingStationTile(v->tile, DirToDiagDir(v->direction))) {
/* Force us to be not overtaking! */
v->SetRoadVehicleOvertaking(0);
} else if (v->HasArticulatedPart() && (v->state >= RVSB_IN_ROAD_STOP || !IsStraightRoadTrackdir((Trackdir)v->state)) && !IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
} else if (v->HasArticulatedPart() && (v->state >= RVSB_IN_ROAD_STOP || !IsStraightRoadTrackdir((Trackdir)v->state)) && !IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) && v->state != RVSB_WORMHOLE) {
/* Articulated RVs may not overtake on corners */
v->SetRoadVehicleOvertaking(0);
} else if (v->HasArticulatedPart() && IsBridgeTile(v->tile) && (IsRoadCustomBridgeHeadTile(v->tile) || IsRoadCustomBridgeHeadTile(GetOtherBridgeEnd(v->tile)))) {
@ -1455,7 +1574,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
/* 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->overtaking_ctr >= v->GetOvertakingCounterThreshold() && v->state < RVSB_IN_ROAD_STOP && IsStraightRoadTrackdir((Trackdir)v->state)) {
if (v->overtaking_ctr >= v->GetOvertakingCounterThreshold() && (v->state == RVSB_WORMHOLE || (v->state < RVSB_IN_ROAD_STOP && IsStraightRoadTrackdir((Trackdir)v->state)))) {
if (IsOneWayRoadTile(v->tile)) {
RoadVehCheckFinishOvertake(v);
} else {
@ -1475,14 +1594,35 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
if (v->state == RVSB_WORMHOLE) {
/* Vehicle is entering a depot or is on a bridge or in a tunnel */
GetNewVehiclePosResult gp = GetNewVehiclePos(v);
if (v->overtaking & 1) {
DiagDirection dir = DirToDiagDir(v->direction);
switch (dir) {
case DIAGDIR_NE:
case DIAGDIR_SW:
SB(gp.y, 0, 4, (_settings_game.vehicle.road_side ^ (dir >> 1) ^ (v->overtaking >> RVS_DRIVE_SIDE)) ? 9 : 5);
break;
case DIAGDIR_SE:
case DIAGDIR_NW:
SB(gp.x, 0, 4, (_settings_game.vehicle.road_side ^ (dir >> 1) ^ (v->overtaking >> RVS_DRIVE_SIDE)) ? 9 : 5);
break;
default:
NOT_REACHED();
}
}
if (v->IsFrontEngine()) {
const Vehicle *u = RoadVehFindCloseTo(v, gp.x, gp.y, v->direction);
RoadVehicle *u = RoadVehFindCloseTo(v, gp.x, gp.y, v->direction);
if (u != nullptr) {
v->cur_speed = u->First()->cur_speed;
u = u->First();
/* There is a vehicle in front overtake it if possible */
byte old_overtaking = v->overtaking;
if (v->overtaking == 0) RoadVehCheckOvertake(v, u);
if (v->overtaking == old_overtaking) {
v->cur_speed = u->cur_speed;
}
return false;
}
}
v->overtaking &= ~1;
if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
if (IsRoadCustomBridgeHeadTile(gp.new_tile)) {
@ -1999,6 +2139,7 @@ void RoadVehicle::SetRoadVehicleOvertaking(byte overtaking)
for (RoadVehicle *u = this; u != nullptr; u = u->Next()) {
u->overtaking = overtaking;
if (u->state == RVSB_WORMHOLE) u->overtaking |= 1;
}
if (IsInsideMM(this->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) RoadStop::GetByTile(this->tile, GetRoadStopType(this->tile))->Enter(this);

@ -117,7 +117,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_DUAL_RAIL_TYPES, XSCF_NULL, 1, 1, "dual_rail_types", nullptr, nullptr, nullptr },
{ XSLFI_CONSIST_SPEED_RD_FLAG, XSCF_NULL, 1, 1, "consist_speed_rd_flag", nullptr, nullptr, nullptr },
{ XSLFI_SAVEGAME_UNIQUE_ID, XSCF_IGNORABLE_ALL, 1, 1, "savegame_unique_id", nullptr, nullptr, nullptr },
{ XSLFI_RV_OVERTAKING, XSCF_NULL, 1, 1, "roadveh_overtaking", nullptr, nullptr, nullptr },
{ XSLFI_RV_OVERTAKING, XSCF_NULL, 2, 2, "roadveh_overtaking", nullptr, nullptr, nullptr },
{ XSLFI_LINKGRAPH_MODES, XSCF_NULL, 1, 1, "linkgraph_modes", nullptr, nullptr, nullptr },
{ XSLFI_GAME_EVENTS, XSCF_NULL, 1, 1, "game_events", nullptr, nullptr, nullptr },
{ XSLFI_ROAD_LAYOUT_CHANGE_CTR, XSCF_NULL, 1, 1, "road_layout_change_ctr", nullptr, nullptr, nullptr },

Loading…
Cancel
Save