Allow drive-through road stops to be one-way

pull/203/head
Jonathan G Rennison 4 years ago
parent 3fa92f5d8e
commit 3a75f13874

@ -892,6 +892,18 @@
<li>m2: index into the array of stations</li>
<li>m3 bits 7..4: persistent random data for railway stations/waypoints and airports)</li>
<li>m3 bits 7..4: <a href="#OwnershipInfo">owner</a> of tram tracks (road stop)</li>
<li style="color: blue">m3 bits 1..0: bits to disallow vehicles to go a specific direction (drive-through road stop)
<table>
<tr>
<td align=left>bit 0: </td>
<td>set = disallow driving in south-west or south-east direction</td>
</tr>
<tr>
<td align=left>bit 1: </td>
<td>set = disallow driving in north-west or north-east direction</td>
</tr>
</table>
</li>
<li>m4: custom station id; 0 means standard graphics</li>
<li>m4: <a href="#RoadType">Roadtype</a> for road stops</li>
<li>m5: graphics index (range from 0..255 for each station type):

@ -238,7 +238,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits">-inherit-</td>
<td class="bits"><span class="free">O</span>XXX XXXX</td>
<td class="bits">-inherit-</td>
<td class="bits">XXXX <span class="free">OOOO</span></td>
<td class="bits">XXXX <span class="free">OO</span><span class="used_p">PP</span></td>
<td class="bits"><span class="free">OO</span>XX XXXX</td>
<td class="bits"><span class="option">~~~~ ~</span>XXX</td>
<td class="bits"><span class="free">OO</span>XX X<span class="free">OOO</span></td>

@ -92,7 +92,7 @@ struct CFollowTrackT
inline static TransportType TT() { return Ttr_type_; }
inline static bool IsWaterTT() { return TT() == TRANSPORT_WATER; }
inline static bool IsRailTT() { return TT() == TRANSPORT_RAIL; }
inline bool IsTram() { return IsRoadTT() && RoadTypeIsTram(RoadVehicle::From(m_veh)->roadtype); }
inline bool IsTram() const { return IsRoadTT() && RoadTypeIsTram(RoadVehicle::From(m_veh)->roadtype); }
inline static bool IsRoadTT() { return TT() == TRANSPORT_ROAD; }
inline static bool Allow90degTurns() { return T90deg_turns_allowed_; }
inline static bool DoTrackMasking() { return Tmask_reserved_tracks; }

@ -369,7 +369,11 @@ static int32 NPFRoadPathCost(AyStar *as, AyStarNode *current, OpenListNode *pare
/* When we're the first road stop in a 'queue' of them we increase
* cost based on the fill percentage of the whole queue. */
const RoadStop::Entry *entry = rs->GetEntry(dir);
cost += entry->GetOccupied() * _settings_game.pf.npf.npf_road_dt_occupied_penalty / entry->GetLength();
if (GetDriveThroughStopDisallowedRoadDirections(tile) != DRD_NONE) {
cost += (entry->GetOccupied() + rs->GetEntry(ReverseDiagDir(dir))->GetOccupied()) * _settings_game.pf.npf.npf_road_dt_occupied_penalty / (2 * entry->GetLength());
} else {
cost += entry->GetOccupied() * _settings_game.pf.npf.npf_road_dt_occupied_penalty / entry->GetLength();
}
}
} else {
/* Increase cost for filled road stops */

@ -68,7 +68,7 @@ protected:
}
/** return one tile cost */
inline int OneTileCost(TileIndex tile, Trackdir trackdir)
inline int OneTileCost(TileIndex tile, Trackdir trackdir, const TrackFollower *tf)
{
int cost = 0;
@ -101,7 +101,11 @@ protected:
/* When we're the first road stop in a 'queue' of them we increase
* cost based on the fill percentage of the whole queue. */
const RoadStop::Entry *entry = rs->GetEntry(dir);
cost += entry->GetOccupied() * Yapf().PfGetSettings().road_stop_occupied_penalty / entry->GetLength();
if (GetDriveThroughStopDisallowedRoadDirections(tile) != DRD_NONE && !tf->IsTram()) {
cost += (entry->GetOccupied() + rs->GetEntry(ReverseDiagDir(dir))->GetOccupied()) * Yapf().PfGetSettings().road_stop_occupied_penalty / (2 * entry->GetLength());
} else {
cost += entry->GetOccupied() * Yapf().PfGetSettings().road_stop_occupied_penalty / entry->GetLength();
}
}
if (predicted_occupied) {
@ -153,7 +157,7 @@ public:
for (;;) {
/* base tile cost depending on distance between edges */
segment_cost += Yapf().OneTileCost(tile, trackdir);
segment_cost += Yapf().OneTileCost(tile, trackdir, tf);
const RoadVehicle *v = Yapf().GetVehicle();
/* we have reached the vehicle's destination - segment should end here to avoid target skipping */

@ -37,6 +37,7 @@
#include "genworld.h"
#include "company_gui.h"
#include "road_func.h"
#include "roadstop_base.h"
#include "table/strings.h"
#include "table/roadtypes.h"
@ -871,7 +872,38 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
}
case MP_STATION: {
if ((GetAnyRoadBits(tile, rtt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT);
if ((GetAnyRoadBits(tile, rtt) & pieces) == pieces) {
if (toggle_drd != DRD_NONE && rtt == RTT_ROAD && IsDriveThroughStopTile(tile)) {
Owner owner = GetRoadOwner(tile, rtt);
if (owner != OWNER_NONE) {
CommandCost ret = CheckOwnership(owner, tile);
if (ret.Failed()) return ret;
}
DisallowedRoadDirections dis_existing = GetDriveThroughStopDisallowedRoadDirections(tile);
DisallowedRoadDirections dis_new = dis_existing ^ toggle_drd;
/* We allow removing disallowed directions to break up
* deadlocks, but adding them can break articulated
* vehicles. As such, only when less is disallowed,
* i.e. bits are removed, we skip the vehicle check. */
if (CountBits(dis_existing) <= CountBits(dis_new)) {
CommandCost ret = EnsureNoVehicleOnGround(tile);
if (ret.Failed()) return ret;
}
if (flags & DC_EXEC) {
RoadStop *rs = RoadStop::GetByTile(tile, GetRoadStopType(tile));
rs->ChangeDriveThroughDisallowedRoadDirections(dis_new);
MarkTileDirtyByTile(tile);
NotifyRoadLayoutChanged(CountBits(dis_existing) > CountBits(dis_new));
}
return CommandCost();
}
return_cmd_error(STR_ERROR_ALREADY_BUILT);
} else {
toggle_drd = DRD_NONE;
}
if (!IsDriveThroughStopTile(tile)) goto do_clear;
RoadBits curbits = AxisToRoadBits(DiagDirToAxis(GetRoadStopDir(tile)));

@ -209,6 +209,103 @@ void RoadStop::ClearDriveThrough()
this->west = nullptr;
}
/**
* Change disallowed road directions of this stop; update other neighbouring stops
* if needed. Also update the length etc.
*/
void RoadStop::ChangeDriveThroughDisallowedRoadDirections(DisallowedRoadDirections drd)
{
assert(this->east != nullptr && this->west != nullptr);
RoadStopType rst = GetRoadStopType(this->xy);
DiagDirection dir = GetRoadStopDir(this->xy);
/* Use absolute so we always go towards the northern tile */
TileIndexDiff offset = abs(TileOffsByDiagDir(dir));
/* Information about the tile north of us */
TileIndex north_tile = this->xy - offset;
bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : nullptr;
/* Information about the tile south of us */
TileIndex south_tile = this->xy + offset;
bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : nullptr;
/* Must only be changed after we determined which neighbours are
* part of our little entry 'queue' */
SetDriveThroughStopDisallowedRoadDirections(this->xy, drd);
if (north) {
/* There is a tile to the north, so we can't clear ourselves. */
if (south) {
/* There are more southern tiles too, they must be split;
* first make the new southern 'base' */
SetBit(rs_south->status, RSSFB_BASE_ENTRY);
rs_south->east = new Entry();
rs_south->west = new Entry();
/* Keep track of the base because we need it later on */
RoadStop *rs_south_base = rs_south;
TileIndex base_tile = south_tile;
/* Make all (even more) southern stops part of the new entry queue */
for (south_tile += offset; IsDriveThroughRoadStopContinuation(base_tile, south_tile); south_tile += offset) {
rs_south = RoadStop::GetByTile(south_tile, rst);
rs_south->east = rs_south_base->east;
rs_south->west = rs_south_base->west;
}
/* We have to rebuild the entries because we cannot easily determine
* how full each part is. So instead of keeping and maintaining a list
* of vehicles and using that to 'rebuild' the occupied state we just
* rebuild it from scratch as that removes lots of maintenance code
* for the vehicle list and it's faster in real games as long as you
* do not keep split and merge road stop every tick by the millions. */
rs_south_base->east->Rebuild(rs_south_base);
rs_south_base->west->Rebuild(rs_south_base);
}
/* Find the other end; the northern most tile */
TileIndex base_tile = north_tile;
for (north_tile -= offset; IsDriveThroughRoadStopContinuation(base_tile, north_tile); north_tile -= offset) {
rs_north = RoadStop::GetByTile(north_tile, rst);
}
assert(HasBit(rs_north->status, RSSFB_BASE_ENTRY));
rs_north->east->Rebuild(rs_north);
rs_north->west->Rebuild(rs_north);
} else if (south) {
/* There is only something to the south. Hand over the base entry */
SetBit(rs_south->status, RSSFB_BASE_ENTRY);
rs_south->east->Rebuild(rs_south);
rs_south->west->Rebuild(rs_south);
} else {
/* We were the last */
delete this->east;
delete this->west;
}
/* Make sure we don't get used for something 'incorrect' */
ClrBit(this->status, RSSFB_BASE_ENTRY);
this->east = nullptr;
this->west = nullptr;
this->MakeDriveThrough();
/* Find the other end; the northern most tile */
TileIndex self_north = this->xy - offset;
RoadStop *rs_self = RoadStop::GetByTile(this->xy, rst);
for (; IsDriveThroughRoadStopContinuation(this->xy, self_north); self_north -= offset) {
rs_self = RoadStop::GetByTile(self_north, rst);
}
/* Update occupancy of stop covering this tile */
assert(HasBit(rs_self->status, RSSFB_BASE_ENTRY));
rs_self->east->Rebuild(rs_self);
rs_self->west->Rebuild(rs_self);
}
/**
* Leave the road stop
* @param rv the vehicle that leaves the stop
@ -221,7 +318,7 @@ void RoadStop::Leave(RoadVehicle *rv)
this->SetEntranceBusy(false);
} else {
/* Otherwise just leave the drive through's entry cache. */
this->GetEntry(DirToDiagDir(rv->direction))->Leave(rv);
this->GetEntry(rv)->Leave(rv);
}
}
@ -249,7 +346,7 @@ bool RoadStop::Enter(RoadVehicle *rv)
}
/* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */
this->GetEntry(DirToDiagDir(rv->direction))->Enter(rv);
this->GetEntry(rv)->Enter(rv);
/* Indicate a drive-through stop */
SetBit(rv->state, RVS_IN_DT_ROAD_STOP);
@ -308,7 +405,8 @@ void RoadStop::Entry::Enter(const RoadVehicle *rv)
GetStationIndex(next) == GetStationIndex(rs) &&
GetStationType(next) == GetStationType(rs) &&
GetRoadStopDir(next) == GetRoadStopDir(rs) &&
IsDriveThroughStopTile(next);
IsDriveThroughStopTile(next) &&
GetDriveThroughStopDisallowedRoadDirections(next) == GetDriveThroughStopDisallowedRoadDirections(rs);
}
typedef std::list<const RoadVehicle *> RVList; ///< A list of road vehicles
@ -329,7 +427,9 @@ Vehicle *FindVehiclesInRoadStop(Vehicle *v, void *data)
{
RoadStopEntryRebuilderHelper *rserh = (RoadStopEntryRebuilderHelper*)data;
/* Not a RV or not in the right direction or crashed :( */
if (DirToDiagDir(v->direction) != rserh->dir || !v->IsPrimaryVehicle() || (v->vehstatus & VS_CRASHED) != 0) return nullptr;
DiagDirection diag_dir = DirToDiagDir(v->direction);
if (RoadVehicle::From(v)->overtaking != 0) diag_dir = ReverseDiagDir(diag_dir);
if (diag_dir != rserh->dir || !v->IsPrimaryVehicle() || (v->vehstatus & VS_CRASHED) != 0) return nullptr;
RoadVehicle *rv = RoadVehicle::From(v);
/* Don't add ones not in a road stop */

@ -14,6 +14,8 @@
#include "core/pool_type.hpp"
#include "core/bitmath_func.hpp"
#include "vehicle_type.h"
#include "roadveh.h"
#include "road_map.h"
typedef Pool<RoadStop, RoadStopID, 32, 64000> RoadStopPool;
extern RoadStopPool _roadstop_pool;
@ -134,8 +136,19 @@ struct RoadStop : RoadStopPool::PoolItem<&_roadstop_pool> {
return HasBit((int)dir, 1) ? this->west : this->east;
}
inline const Entry *GetEntry(const RoadVehicle *rv) const {
DiagDirection diag_dir = DirToDiagDir(rv->direction);
return this->GetEntry(rv->overtaking != 0 ? ReverseDiagDir(diag_dir) : diag_dir);
}
inline Entry *GetEntry(const RoadVehicle *rv) {
DiagDirection diag_dir = DirToDiagDir(rv->direction);
return this->GetEntry(rv->overtaking != 0 ? ReverseDiagDir(diag_dir) : diag_dir);
}
void MakeDriveThrough();
void ClearDriveThrough();
void ChangeDriveThroughDisallowedRoadDirections(DisallowedRoadDirections drd);
void Leave(RoadVehicle *rv);
bool Enter(RoadVehicle *rv);

@ -171,12 +171,7 @@ struct RoadVehicle FINAL : public GroundVehicle<RoadVehicle, VEH_ROAD> {
return RV_OVERTAKE_TIMEOUT + (this->gcache.cached_total_length / 2) - (VEHICLE_LENGTH / 2);
}
inline void SetRoadVehicleOvertaking(byte overtaking)
{
for (RoadVehicle *u = this; u != nullptr; u = u->Next()) {
u->overtaking = overtaking;
}
}
void SetRoadVehicleOvertaking(byte overtaking);
protected: // These functions should not be called outside acceleration code.

@ -373,6 +373,13 @@ bool RoadVehicle::FindClosestDepot(TileIndex *location, DestinationID *destinati
return true;
}
static bool IsOneWayRoadTile(TileIndex tile)
{
if (IsNormalRoadTile(tile) && GetDisallowedRoadDirections(tile) != DRD_NONE) return true;
if (IsDriveThroughStopTile(tile) && GetDriveThroughStopDisallowedRoadDirections(tile) != DRD_NONE) return true;
return false;
}
/**
* Turn a roadvehicle around.
* @param tile unused
@ -401,7 +408,7 @@ CommandCost CmdTurnRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
return CMD_ERROR;
}
if (IsNormalRoadTile(v->tile) && GetDisallowedRoadDirections(v->tile) != DRD_NONE) return CMD_ERROR;
if (IsOneWayRoadTile(v->tile)) return CMD_ERROR;
if (IsTileType(v->tile, MP_TUNNELBRIDGE) && DirToDiagDir(v->direction) == GetTunnelBridgeDirection(v->tile)) return CMD_ERROR;
@ -848,13 +855,7 @@ static Vehicle *EnumFindVehBlockingOvertake(Vehicle *v, void *data)
return v;
}
/**
* Check if overtaking is possible on a piece of track
*
* @param od Information about the tile and the involved vehicles
* @return true if we have to abort overtaking
*/
static bool CheckRoadBlockedForOvertaking(OvertakeData *od)
static bool CheckRoadInfraUnsuitableForOvertaking(OvertakeData *od)
{
if (!HasTileAnyRoadType(od->tile, od->v->compatible_roadtypes)) return true;
TrackStatus ts = GetTileTrackStatus(od->tile, TRANSPORT_ROAD, GetRoadTramType(od->v->roadtype));
@ -865,22 +866,45 @@ static bool CheckRoadBlockedForOvertaking(OvertakeData *od)
/* Track does not continue along overtaking direction || track has junction || levelcrossing is barred */
if (!HasBit(trackdirbits, od->trackdir) || (trackbits & ~TRACK_BIT_CROSS) || (red_signals != TRACKDIR_BIT_NONE)) return true;
return false;
}
/**
* Check if overtaking is possible on a piece of track
*
* @param od Information about the tile and the involved vehicles
* @return true if we have to abort overtaking
*/
static bool CheckRoadBlockedForOvertaking(OvertakeData *od)
{
/* Are there more vehicles on the tile except the two vehicles involved in overtaking */
return HasVehicleOnPos(od->tile, VEH_ROAD, od, EnumFindVehBlockingOvertake);
}
static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
/**
* Check if overtaking is possible on a piece of track
*
* @param od Information about the tile and the involved vehicles
* @return true if we have to abort overtaking
*/
static bool IsNonOvertakingStationTile(TileIndex tile, DiagDirection diag_dir)
{
OvertakeData od;
od.v = v;
od.u = u;
if (!IsTileType(tile, MP_STATION)) return false;
if (!IsDriveThroughStopTile(tile)) return true;
const DisallowedRoadDirections diagdir_to_drd[DIAGDIR_END] = { DRD_NORTHBOUND, DRD_NORTHBOUND, DRD_SOUTHBOUND, DRD_SOUTHBOUND };
return GetDriveThroughStopDisallowedRoadDirections(tile) != diagdir_to_drd[diag_dir];
}
static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
{
/* Trams can't overtake other trams */
if (RoadTypeIsTram(v->roadtype)) return;
/* Vehicles are not driving in same direction || direction is not a diagonal direction */
if (v->direction != u->direction || !(v->direction & 1)) return;
/* Don't overtake in stations */
if (IsTileType(u->tile, MP_STATION)) return;
if (IsNonOvertakingStationTile(u->tile, DirToDiagDir(u->direction))) return;
/* If not permitted, articulated road vehicles can't overtake anything. */
if (!_settings_game.vehicle.roadveh_articulated_overtaking && v->HasArticulatedPart()) return;
@ -888,21 +912,21 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
/* Don't overtake if the vehicle is broken or about to break down */
if (v->breakdown_ctr != 0) return;
/* Vehicles are not driving in same direction || direction is not a diagonal direction */
if (v->direction != u->direction || !(v->direction & 1)) return;
/* Vehicles chain is too long to overtake */
if (v->GetOvertakingCounterThreshold() > 255) return;
for (RoadVehicle *w = v; w != nullptr; w = w->Next()) {
/* Don't overtake in stations */
if (IsTileType(w->tile, MP_STATION)) return;
if (IsNonOvertakingStationTile(w->tile, DirToDiagDir(w->direction))) return;
/* 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 */
if (w->state >= RVSB_IN_ROAD_STOP || !IsStraightRoadTrackdir((Trackdir)(w->state & RVSB_TRACKDIR_MASK))) return;
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)) {
return;
}
}
/* Can't overtake a vehicle that is moving faster than us. If the vehicle in front is
@ -915,6 +939,9 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
return;
}
OvertakeData od;
od.v = v;
od.u = u;
od.trackdir = DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
/* Are the current and the next tile suitable for overtaking?
@ -928,12 +955,22 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
TileIndexDiff check_tile_diff = TileOffsByDiagDir(DirToDiagDir(v->direction));
for (; tile_count != 0; tile_count--, check_tile += check_tile_diff) {
od.tile = check_tile;
if (CheckRoadInfraUnsuitableForOvertaking(&od)) return;
if (IsDriveThroughStopTile(check_tile) && GetDriveThroughStopDisallowedRoadDirections(check_tile) != DRD_NONE) {
const RoadStop *rs = RoadStop::GetByTile(check_tile, GetRoadStopType(check_tile));
DiagDirection dir = DirToDiagDir(v->direction);
const RoadStop::Entry *entry = rs->GetEntry(dir);
const RoadStop::Entry *opposite_entry = rs->GetEntry(ReverseDiagDir(dir));
if (entry->GetOccupied() < opposite_entry->GetOccupied()) return;
break;
}
if (CheckRoadBlockedForOvertaking(&od)) return;
}
tile_count = v->gcache.cached_total_length / 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 (CheckRoadInfraUnsuitableForOvertaking(&od)) return;
if (CheckRoadBlockedForOvertaking(&od)) return;
}
@ -1239,14 +1276,37 @@ static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadType rt, RoadB
return ret.Succeeded();
}
static bool IsRoadVehicleOnOtherSideOfRoad(const RoadVehicle *v)
{
bool is_right;
switch (DirToDiagDir(v->direction)) {
case DIAGDIR_NE:
is_right = ((TILE_UNIT_MASK & v->y_pos) == 9);
break;
case DIAGDIR_SE:
is_right = ((TILE_UNIT_MASK & v->x_pos) == 9);
break;
case DIAGDIR_SW:
is_right = ((TILE_UNIT_MASK & v->y_pos) == 5);
break;
case DIAGDIR_NW:
is_right = ((TILE_UNIT_MASK & v->x_pos) == 5);
break;
default:
NOT_REACHED();
}
return is_right != (bool) _settings_game.vehicle.road_side;
}
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 (IsTileType(v->tile, MP_STATION)) {
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))) {
} 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)) {
/* Articulated RVs may not overtake on corners */
v->SetRoadVehicleOvertaking(0);
} else if (v->HasArticulatedPart() && IsBridgeTile(v->tile) && (IsRoadCustomBridgeHeadTile(v->tile) || IsRoadCustomBridgeHeadTile(GetOtherBridgeEnd(v->tile)))) {
@ -1258,6 +1318,9 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
* overtake if we are on straight roads */
if (v->overtaking_ctr >= v->GetOvertakingCounterThreshold() && v->state < RVSB_IN_ROAD_STOP && IsStraightRoadTrackdir((Trackdir)v->state)) {
v->SetRoadVehicleOvertaking(0);
} else if (v->overtaking_ctr == 0) {
/* prevent overflow issues */
v->overtaking_ctr = 255;
}
}
}
@ -1396,7 +1459,7 @@ again:
v->cur_speed = 0;
return false;
}
} else if (IsNormalRoadTile(v->tile) && GetDisallowedRoadDirections(v->tile) != DRD_NONE) {
} else if (IsOneWayRoadTile(v->tile)) {
v->cur_speed = 0;
return false;
} else {
@ -1572,14 +1635,17 @@ again:
if (u != nullptr) {
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 == 0) v->cur_speed = u->cur_speed;
if (v->overtaking == old_overtaking) v->cur_speed = u->cur_speed;
/* In case an RV is stopped in a road stop, why not try to load? */
if (v->cur_speed == 0 && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) &&
v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile), false) &&
IsInfraTileUsageAllowed(VEH_ROAD, v->owner, v->tile) && !v->current_order.IsType(OT_LEAVESTATION) &&
GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) {
byte cur_overtaking = IsRoadVehicleOnOtherSideOfRoad(v) ? RVSB_DRIVE_SIDE : 0;
if (cur_overtaking != v->overtaking) v->SetRoadVehicleOvertaking(cur_overtaking);
Station *st = Station::GetByTile(v->tile);
v->last_station_visited = st->index;
RoadVehArrivesAt(v, st);
@ -1787,6 +1853,17 @@ void RoadVehicle::SetDestTile(TileIndex tile)
this->dest_tile = tile;
}
void RoadVehicle::SetRoadVehicleOvertaking(byte overtaking)
{
if (IsInsideMM(this->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) RoadStop::GetByTile(this->tile, GetRoadStopType(this->tile))->Leave(this);
for (RoadVehicle *u = this; u != nullptr; u = u->Next()) {
u->overtaking = overtaking;
}
if (IsInsideMM(this->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) RoadStop::GetByTile(this->tile, GetRoadStopType(this->tile))->Enter(this);
}
static void CheckIfRoadVehNeedsService(RoadVehicle *v)
{
/* If we already got a slot at a stop, use that FIRST, and go to a depot later */

@ -3787,6 +3787,14 @@ bool AfterLoadGame()
}
}
if (SlXvIsFeatureMissing(XSLFI_ONE_WAY_DT_ROAD_STOP)) {
for (TileIndex t = 0; t < map_size; t++) {
if (IsDriveThroughStopTile(t)) {
SetDriveThroughStopDisallowedRoadDirections(t, DRD_NONE);
}
}
}
InitializeRoadGUI();
/* This needs to be done after conversion. */

@ -136,6 +136,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_THROUGH_TRAIN_DEPOT, XSCF_NULL, 1, 1, "drive_through_train_depot", nullptr, nullptr, nullptr },
{ XSLFI_MORE_VEHICLE_ORDERS, XSCF_NULL, 1, 1, "more_veh_orders", nullptr, nullptr, nullptr },
{ XSLFI_ORDER_FLAGS_EXTRA, XSCF_NULL, 1, 1, "order_flags_extra", nullptr, nullptr, nullptr },
{ XSLFI_ONE_WAY_DT_ROAD_STOP, XSCF_NULL, 1, 1, "one_way_dt_road_stop", nullptr, nullptr, nullptr },
{ XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker
};

@ -90,6 +90,7 @@ enum SlXvFeatureIndex {
XSLFI_THROUGH_TRAIN_DEPOT, ///< Drive-through train depots
XSLFI_MORE_VEHICLE_ORDERS, ///< More vehicle orders - VehicleOrderID is 16 bits instead of 8
XSLFI_ORDER_FLAGS_EXTRA, ///< Order flags field extra size
XSLFI_ONE_WAY_DT_ROAD_STOP, ///< One-way drive-through road stops
XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit
XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk

@ -115,6 +115,7 @@
uint dir_2 = 2 ^ dir_1;
DisallowedRoadDirections drd2 = IsNormalRoadTile(t2) ? GetDisallowedRoadDirections(t2) : DRD_NONE;
if (IsDriveThroughStopTile(t2)) drd2 = GetDriveThroughStopDisallowedRoadDirections(t2);
return HasBit(r1, dir_1) && HasBit(r2, dir_2) && drd2 != DRD_BOTH && drd2 != (dir_1 > dir_2 ? DRD_SOUTHBOUND : DRD_NORTHBOUND);
}

@ -1080,11 +1080,6 @@ static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags
if (RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
CommandCost ret = CheckOwnership(road_owner);
if (ret.Failed()) return ret;
}
cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
} else if (RoadTypeIsRoad(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
@ -2034,6 +2029,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
DisallowedRoadDirections drd = IsNormalRoadTile(cur_tile) ? GetDisallowedRoadDirections(cur_tile) : DRD_NONE;
if (IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile)) {
RemoveRoadStop(cur_tile, flags);
@ -2071,6 +2067,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, 2);
MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, rs_type, road_rt, tram_rt, axis);
SetDriveThroughStopDisallowedRoadDirections(cur_tile, drd);
road_stop->MakeDriveThrough();
} else {
if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
@ -2246,6 +2243,7 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
RoadBits road_bits = ROAD_NONE;
RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
DisallowedRoadDirections drd = DRD_NONE;
if (IsDriveThroughStopTile(cur_tile)) {
FOR_ALL_ROADTRAMTYPES(rtt) {
road_type[rtt] = GetRoadType(cur_tile, rtt);
@ -2255,6 +2253,7 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
if (!keep_drive_through_roads && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
}
road_bits = AxisToRoadBits(DiagDirToAxis(GetRoadStopDir(cur_tile)));
drd = GetDriveThroughStopDisallowedRoadDirections(cur_tile);
}
CommandCost ret = RemoveRoadStop(cur_tile, flags);
@ -2269,6 +2268,7 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
if ((flags & DC_EXEC) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
if (drd != DRD_NONE) SetDisallowedRoadDirections(cur_tile, drd);
/* Update company infrastructure counts. */
int count = CountBits(road_bits);
@ -3270,6 +3270,11 @@ draw_default_foundation:
uint sprite_offset = axis == AXIS_X ? 1 : 0;
DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
DisallowedRoadDirections drd = GetDriveThroughStopDisallowedRoadDirections(ti->tile);
if (drd != DRD_NONE) {
DrawGroundSpriteAt(SPR_ONEWAY_BASE + drd - 1 + ((axis == AXIS_X) ? 0 : 3), PAL_NONE, 8, 8, 0);
}
} else {
/* Non-drivethrough road stops are only valid for roads. */
assert_tile(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE, ti->tile);
@ -3460,23 +3465,24 @@ static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
{
TrackBits trackbits = TRACK_BIT_NONE;
TrackdirBits trackdirbits = TRACKDIR_BIT_NONE;
switch (mode) {
case TRANSPORT_RAIL:
if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
trackbits = TrackToTrackBits(GetRailStationTrack(tile));
trackdirbits = TrackToTrackdirBits(GetRailStationTrack(tile));
}
break;
case TRANSPORT_WATER:
/* buoy is coded as a station, it is always on open water */
if (IsBuoy(tile)) {
trackbits = TRACK_BIT_ALL;
TrackBits trackbits = TRACK_BIT_ALL;
/* remove tracks that connect NE map edge */
if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
/* remove tracks that connect NW map edge */
if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
trackdirbits = TrackBitsToTrackdirBits(trackbits);
}
break;
@ -3492,7 +3498,13 @@ static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode
if (axis != DiagDirToAxis(side) || (IsStandardRoadStopTile(tile) && dir != side)) break;
}
trackbits = AxisToTrackBits(axis);
TrackBits trackbits = AxisToTrackBits(axis);
if (IsDriveThroughStopTile(tile)) {
const uint drd_to_multiplier[DRD_END] = { 0x101, 0x100, 0x1, 0x0 };
trackdirbits = (TrackdirBits)(trackbits * drd_to_multiplier[GetDriveThroughStopDisallowedRoadDirections(tile)]);
} else {
trackdirbits = TrackBitsToTrackdirBits(trackbits);
}
}
break;
@ -3500,7 +3512,7 @@ static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode
break;
}
return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), TRACKDIR_BIT_NONE);
return CombineTrackStatus(trackdirbits, TRACKDIR_BIT_NONE);
}

@ -235,6 +235,29 @@ static inline bool IsDriveThroughStopTile(TileIndex t)
return IsRoadStopTile(t) && GetStationGfx(t) >= GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET;
}
/**
* Gets the disallowed directions
* @param t the tile to get the directions from
* @return the disallowed directions
*/
static inline DisallowedRoadDirections GetDriveThroughStopDisallowedRoadDirections(TileIndex t)
{
assert_tile(IsDriveThroughStopTile(t), t);
return (DisallowedRoadDirections)GB(_m[t].m3, 0, 2);
}
/**
* Sets the disallowed directions
* @param t the tile to set the directions for
* @param drd the disallowed directions
*/
static inline void SetDriveThroughStopDisallowedRoadDirections(TileIndex t, DisallowedRoadDirections drd)
{
assert_tile(IsDriveThroughStopTile(t), t);
assert(drd < DRD_END);
SB(_m[t].m3, 0, 2, drd);
}
/**
* Get the station graphics of this airport tile
* @param t the tile to query

Loading…
Cancel
Save