OpenTTD Source  20241120-master-g6d3adc6169
roadstop.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "stdafx.h"
11 #include "roadveh.h"
12 #include "core/pool_func.hpp"
13 #include "roadstop_base.h"
14 #include "station_base.h"
15 #include "vehicle_func.h"
16 
17 #include "safeguards.h"
18 
22 
23 
27 {
28  /* When we are the head we need to free the entries */
29  if (HasBit(this->status, RSSFB_BASE_ENTRY)) {
30  delete this->east;
31  delete this->west;
32  }
33 
34  if (CleaningPool()) return;
35 }
36 
43 {
44  for (RoadStop *rs = this->next; rs != nullptr; rs = rs->next) {
45  /* The vehicle cannot go to this roadstop (different roadtype) */
46  if (!HasTileAnyRoadType(rs->xy, v->compatible_roadtypes)) continue;
47  /* The vehicle is articulated and can therefore not go to a standard road stop. */
48  if (IsBayRoadStopTile(rs->xy) && v->HasArticulatedPart()) continue;
49 
50  /* The vehicle can actually go to this road stop. So, return it! */
51  return rs;
52  }
53 
54  return nullptr;
55 }
56 
63 {
64  assert(this->east == nullptr && this->west == nullptr);
65 
66  RoadStopType rst = GetRoadStopType(this->xy);
67  Axis axis = GetDriveThroughStopAxis(this->xy);
68  TileIndexDiff offset = TileOffsByAxis(axis);
69 
70  /* Information about the tile north of us */
71  TileIndex north_tile = this->xy - offset;
72  bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
73  RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : nullptr;
74 
75  /* Information about the tile south of us */
76  TileIndex south_tile = this->xy + offset;
77  bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
78  RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : nullptr;
79 
80  /* Amount of road stops that will be added to the 'northern' head */
81  int added = 1;
82  if (north && rs_north->east != nullptr) { // (east != nullptr) == (west != nullptr)
83  /* There is a more northern one, so this can join them */
84  this->east = rs_north->east;
85  this->west = rs_north->west;
86 
87  if (south && rs_south->east != nullptr) { // (east != nullptr) == (west != nullptr)
88  /* There more southern tiles too, they must 'join' us too */
89  ClrBit(rs_south->status, RSSFB_BASE_ENTRY);
90  this->east->occupied += rs_south->east->occupied;
91  this->west->occupied += rs_south->west->occupied;
92 
93  /* Free the now unneeded entry structs */
94  delete rs_south->east;
95  delete rs_south->west;
96 
97  /* Make all 'children' of the southern tile take the new master */
98  for (; IsDriveThroughRoadStopContinuation(this->xy, south_tile); south_tile += offset) {
99  rs_south = RoadStop::GetByTile(south_tile, rst);
100  if (rs_south->east == nullptr) break;
101  rs_south->east = rs_north->east;
102  rs_south->west = rs_north->west;
103  added++;
104  }
105  }
106  } else if (south && rs_south->east != nullptr) { // (east != nullptr) == (west != nullptr)
107  /* There is one to the south, but not to the north... so we become 'parent' */
108  this->east = rs_south->east;
109  this->west = rs_south->west;
111  ClrBit(rs_south->status, RSSFB_BASE_ENTRY);
112  } else {
113  /* We are the only... so we are automatically the master */
114  this->east = new Entry();
115  this->west = new Entry();
117  }
118 
119  /* Now update the lengths */
120  added *= TILE_SIZE;
121  this->east->length += added;
122  this->west->length += added;
123 }
124 
130 {
131  assert(this->east != nullptr && this->west != nullptr);
132 
133  RoadStopType rst = GetRoadStopType(this->xy);
134  Axis axis = GetDriveThroughStopAxis(this->xy);
135  TileIndexDiff offset = TileOffsByAxis(axis);
136 
137  /* Information about the tile north of us */
138  TileIndex north_tile = this->xy - offset;
139  bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
140  RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : nullptr;
141 
142  /* Information about the tile south of us */
143  TileIndex south_tile = this->xy + offset;
144  bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
145  RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : nullptr;
146 
147  /* Must only be cleared after we determined which neighbours are
148  * part of our little entry 'queue' */
149  DoClearSquare(this->xy);
150 
151  if (north) {
152  /* There is a tile to the north, so we can't clear ourselves. */
153  if (south) {
154  /* There are more southern tiles too, they must be split;
155  * first make the new southern 'base' */
156  SetBit(rs_south->status, RSSFB_BASE_ENTRY);
157  rs_south->east = new Entry();
158  rs_south->west = new Entry();
159 
160  /* Keep track of the base because we need it later on */
161  RoadStop *rs_south_base = rs_south;
162  TileIndex base_tile = south_tile;
163 
164  /* Make all (even more) southern stops part of the new entry queue */
165  for (south_tile += offset; IsDriveThroughRoadStopContinuation(base_tile, south_tile); south_tile += offset) {
166  rs_south = RoadStop::GetByTile(south_tile, rst);
167  rs_south->east = rs_south_base->east;
168  rs_south->west = rs_south_base->west;
169  }
170 
171  /* Find the other end; the northern most tile */
172  for (; IsDriveThroughRoadStopContinuation(base_tile, north_tile); north_tile -= offset) {
173  rs_north = RoadStop::GetByTile(north_tile, rst);
174  }
175 
176  /* We have to rebuild the entries because we cannot easily determine
177  * how full each part is. So instead of keeping and maintaining a list
178  * of vehicles and using that to 'rebuild' the occupied state we just
179  * rebuild it from scratch as that removes lots of maintenance code
180  * for the vehicle list and it's faster in real games as long as you
181  * do not keep split and merge road stop every tick by the millions. */
182  rs_south_base->east->Rebuild(rs_south_base);
183  rs_south_base->west->Rebuild(rs_south_base);
184 
185  assert(HasBit(rs_north->status, RSSFB_BASE_ENTRY));
186  rs_north->east->Rebuild(rs_north);
187  rs_north->west->Rebuild(rs_north);
188  } else {
189  /* Only we left, so simple update the length. */
190  rs_north->east->length -= TILE_SIZE;
191  rs_north->west->length -= TILE_SIZE;
192  }
193  } else if (south) {
194  /* There is only something to the south. Hand over the base entry */
195  SetBit(rs_south->status, RSSFB_BASE_ENTRY);
196  rs_south->east->length -= TILE_SIZE;
197  rs_south->west->length -= TILE_SIZE;
198  } else {
199  /* We were the last */
200  delete this->east;
201  delete this->west;
202  }
203 
204  /* Make sure we don't get used for something 'incorrect' */
206  this->east = nullptr;
207  this->west = nullptr;
208 }
209 
215 {
216  if (IsBayRoadStopTile(rv->tile)) {
217  /* Vehicle is leaving a road stop tile, mark bay as free */
219  this->SetEntranceBusy(false);
220  } else {
221  /* Otherwise just leave the drive through's entry cache. */
222  this->GetEntry(DirToDiagDir(rv->direction))->Leave(rv);
223  }
224 }
225 
232 {
233  if (IsBayRoadStopTile(this->xy)) {
234  /* For normal (non drive-through) road stops
235  * Check if station is busy or if there are no free bays or whether it is a articulated vehicle. */
236  if (this->IsEntranceBusy() || !this->HasFreeBay() || rv->HasArticulatedPart()) return false;
237 
239 
240  /* Allocate a bay and update the road state */
241  uint bay_nr = this->AllocateBay();
242  SB(rv->state, RVS_USING_SECOND_BAY, 1, bay_nr);
243 
244  /* Mark the station entrance as busy */
245  this->SetEntranceBusy(true);
246  return true;
247  }
248 
249  /* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */
250  this->GetEntry(DirToDiagDir(rv->direction))->Enter(rv);
251 
252  /* Indicate a drive-through stop */
254  return true;
255 }
256 
265 {
266  const Station *st = Station::GetByTile(tile);
267 
268  for (RoadStop *rs = st->GetPrimaryRoadStop(type);; rs = rs->next) {
269  if (rs->xy == tile) return rs;
270  assert(rs->next != nullptr);
271  }
272 }
273 
279 {
280  this->occupied -= rv->gcache.cached_total_length;
281  assert(this->occupied >= 0);
282 }
283 
289 {
290  /* we cannot assert on this->occupied < this->length because of the
291  * remote possibility that RVs are running through each other when
292  * trying to prevention an infinite jam. */
293  this->occupied += rv->gcache.cached_total_length;
294 }
295 
304 {
305  return IsTileType(next, MP_STATION) &&
310 }
311 
312 typedef std::list<const RoadVehicle *> RVList;
313 
318 };
319 
327 {
329  /* Not a RV or not in the right direction or crashed :( */
330  if (v->type != VEH_ROAD || DirToDiagDir(v->direction) != rserh->dir || !v->IsPrimaryVehicle() || (v->vehstatus & VS_CRASHED) != 0) return nullptr;
331 
333  /* Don't add ones not in a road stop */
334  if (rv->state < RVSB_IN_ROAD_STOP) return nullptr;
335 
336  /* Do not add duplicates! */
337  for (const auto &it : rserh->vehicles) {
338  if (rv == it) return nullptr;
339  }
340 
341  rserh->vehicles.push_back(rv);
342  return nullptr;
343 }
344 
352 {
353  switch (axis) {
354  case AXIS_X: return east ? DIAGDIR_NE : DIAGDIR_SW;
355  case AXIS_Y: return east ? DIAGDIR_SE : DIAGDIR_NW;
356  default: NOT_REACHED();
357  }
358 }
359 
365 {
366  assert(HasBit(rs->status, RSSFB_BASE_ENTRY));
367 
368  Axis axis = GetDriveThroughStopAxis(rs->xy);
369 
371  rserh.dir = GetEntryDirection(rs->east == this, axis);
372 
373  this->length = 0;
374  TileIndexDiff offset = TileOffsByAxis(axis);
375  for (TileIndex tile = rs->xy; IsDriveThroughRoadStopContinuation(rs->xy, tile); tile += offset) {
376  this->length += TILE_SIZE;
378  }
379 
380  this->occupied = 0;
381  for (const auto &it : rserh.vehicles) {
382  this->occupied += it->gcache.cached_total_length;
383  }
384 }
385 
386 
392 {
393  if (!HasBit(rs->status, RSSFB_BASE_ENTRY)) return;
394 
395  /* The tile 'before' the road stop must not be part of this 'line' */
396  assert(IsDriveThroughStopTile(rs->xy));
398 
399  Entry temp;
400  temp.Rebuild(rs);
401  if (temp.length != this->length || temp.occupied != this->occupied) NOT_REACHED();
402 }
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SB(T &x, const uint8_t s, const uint8_t n, const U d)
Set n bits in x starting at bit s to d.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
Axis
Allow incrementing of DiagDirDiff variables.
@ AXIS_X
The X axis.
@ AXIS_Y
The y axis.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_NW
Northwest.
@ DIAGDIR_SE
Southeast.
@ DIAGDIR_SW
Southwest.
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition: map_func.h:552
int32_t TileIndexDiff
An offset value between two tiles.
Definition: map_type.h:23
Some methods of Pool are placed here in order to reduce compilation time and binary size.
#define INSTANTIATE_POOL_METHODS(name)
Force instantiation of pool methods so we don't get linker errors.
Definition: pool_func.hpp:237
bool HasTileAnyRoadType(Tile t, RoadTypes rts)
Check if a tile has one of the specified road types.
Definition: road_map.h:222
static DiagDirection GetEntryDirection(bool east, Axis axis)
Get the DiagDirection for entering the drive through stop from the given 'side' (east or west) on the...
Definition: roadstop.cpp:351
std::list< const RoadVehicle * > RVList
A list of road vehicles.
Definition: roadstop.cpp:312
RoadStopPool _roadstop_pool("RoadStop")
The pool of roadstops.
Vehicle * FindVehiclesInRoadStop(Vehicle *v, void *data)
Add road vehicles to the station's list if needed.
Definition: roadstop.cpp:326
Base class for roadstops.
Road vehicle states.
@ RVSB_IN_ROAD_STOP
The vehicle is in a road stop.
Definition: roadveh.h:49
@ RVS_USING_SECOND_BAY
Only used while in a road stop.
Definition: roadveh.h:42
@ RVS_IN_DT_ROAD_STOP
The vehicle is in a drive-through road stop.
Definition: roadveh.h:46
@ RVS_IN_ROAD_STOP
The vehicle is in a road stop.
Definition: roadveh.h:45
A number of safeguards to prevent using unsafe methods.
Base classes/functions for stations.
StationType GetStationType(Tile t)
Get the station type of this tile.
Definition: station_map.h:44
bool IsBayRoadStopTile(Tile t)
Is tile t a bay (non-drive through) road stop station?
Definition: station_map.h:266
bool IsDriveThroughStopTile(Tile t)
Is tile t a drive through road stop station or waypoint?
Definition: station_map.h:276
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition: station_map.h:28
Axis GetDriveThroughStopAxis(Tile t)
Gets the axis of the drive through stop.
Definition: station_map.h:356
RoadStopType GetRoadStopType(Tile t)
Get the road stop type of this tile.
Definition: station_map.h:56
RoadStopType
Types of RoadStops.
Definition: station_type.h:45
Definition of base types and functions in a cross-platform compatible way.
VehicleType type
Type of vehicle.
Definition: vehicle_type.h:51
uint16_t cached_total_length
Length of the whole vehicle (valid only for the first engine).
GroundVehicleCache gcache
Cache of often calculated values.
Base class for all pools.
Definition: pool_type.hpp:80
Helper for finding RVs in a road stop.
Definition: roadstop.cpp:315
RVList vehicles
The list of vehicles to possibly add to.
Definition: roadstop.cpp:316
DiagDirection dir
The direction the vehicle has to face to be added.
Definition: roadstop.cpp:317
Container for each entry point of a drive through road stop.
Definition: roadstop_base.h:33
int length
The length of the stop in tile 'units'.
Definition: roadstop_base.h:35
void Leave(const RoadVehicle *rv)
Leave the road stop.
Definition: roadstop.cpp:278
void Enter(const RoadVehicle *rv)
Enter the road stop.
Definition: roadstop.cpp:288
void Rebuild(const RoadStop *rs)
Rebuild, from scratch, the vehicles and other metadata on this stop.
Definition: roadstop.cpp:364
void CheckIntegrity(const RoadStop *rs) const
Check the integrity of the data in this struct.
Definition: roadstop.cpp:391
int occupied
The amount of occupied stop in tile 'units'.
Definition: roadstop_base.h:36
A Stop for a Road Vehicle.
Definition: roadstop_base.h:22
void SetEntranceBusy(bool busy)
Makes an entrance occupied or free.
Entry * east
The vehicles that entered from the east.
const Entry * GetEntry(DiagDirection dir) const
Get the drive through road stop entry struct for the given direction.
@ RSSFB_BASE_ENTRY
Non-zero when the entries on this road stop are the primary, i.e. the ones to delete.
Definition: roadstop_base.h:26
void Leave(RoadVehicle *rv)
Leave the road stop.
Definition: roadstop.cpp:214
RoadStop * next
Next stop of the given type at this station.
Definition: roadstop_base.h:70
uint AllocateBay()
Allocates a bay.
RoadStop * GetNextRoadStop(const struct RoadVehicle *v) const
Get the next road stop accessible by this vehicle.
Definition: roadstop.cpp:42
bool HasFreeBay() const
Checks whether there is a free bay in this road stop.
Definition: roadstop_base.h:84
bool Enter(RoadVehicle *rv)
Enter the road stop.
Definition: roadstop.cpp:231
void FreeBay(uint nr)
Frees the given bay.
TileIndex xy
Position on the map.
Definition: roadstop_base.h:69
bool IsEntranceBusy() const
Checks whether the entrance of the road stop is occupied by a vehicle.
static bool IsDriveThroughRoadStopContinuation(TileIndex rs, TileIndex next)
Checks whether the 'next' tile is still part of the road same drive through stop 'rs' in the same dir...
Definition: roadstop.cpp:303
uint8_t status
Current status of the Stop,.
Definition: roadstop_base.h:68
static RoadStop * GetByTile(TileIndex tile, RoadStopType type)
Find a roadstop at given tile.
Definition: roadstop.cpp:264
void MakeDriveThrough()
Join this road stop to another 'base' road stop if possible; fill all necessary data to become an act...
Definition: roadstop.cpp:62
Entry * west
The vehicles that entered from the west.
void ClearDriveThrough()
Prepare for removal of this stop; update other neighbouring stops if needed.
Definition: roadstop.cpp:129
Buses, trucks and trams belong to this class.
Definition: roadveh.h:98
uint8_t state
Definition: roadveh.h:100
RoadTypes compatible_roadtypes
NOSAVE: Roadtypes this consist is powered on.
Definition: roadveh.h:110
static Station * GetByTile(TileIndex tile)
Get the station belonging to a specific tile.
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
Station data structure.
Definition: station_base.h:439
Vehicle data structure.
Definition: vehicle_base.h:244
Direction direction
facing
Definition: vehicle_base.h:307
bool HasArticulatedPart() const
Check if an engine has an articulated part.
Definition: vehicle_base.h:963
uint8_t vehstatus
Status.
Definition: vehicle_base.h:354
virtual bool IsPrimaryVehicle() const
Whether this is the primary vehicle in the chain.
Definition: vehicle_base.h:477
TileIndex tile
Current tile index.
Definition: vehicle_base.h:264
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition: tile_map.h:150
static const uint TILE_SIZE
Tile size in world coordinates.
Definition: tile_type.h:15
@ MP_STATION
A tile of a station.
Definition: tile_type.h:53
void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
Find a vehicle from a specific location.
Definition: vehicle.cpp:505
@ VS_CRASHED
Vehicle is crashed.
Definition: vehicle_base.h:40
Functions related to vehicles.
@ VEH_ROAD
Road vehicle type.
Definition: vehicle_type.h:25