OpenTTD Source 20250312-master-gcdcc6b491d
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{
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
311
312typedef std::list<const RoadVehicle *> RVList;
313
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.Test(VehState::Crashed)) 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
365void RoadStop::Entry::Rebuild(const RoadStop *rs, int side)
366{
367 assert(HasBit(rs->status, RSSFB_BASE_ENTRY));
368
369 Axis axis = GetDriveThroughStopAxis(rs->xy);
370 if (side == -1) side = (rs->east == this);
371
373 rserh.dir = GetEntryDirection(side, axis);
374
375 this->length = 0;
376 TileIndexDiff offset = TileOffsByAxis(axis);
377 for (TileIndex tile = rs->xy; IsDriveThroughRoadStopContinuation(rs->xy, tile); tile += offset) {
378 this->length += TILE_SIZE;
380 }
381
382 this->occupied = 0;
383 for (const auto &it : rserh.vehicles) {
384 this->occupied += it->gcache.cached_total_length;
385 }
386}
387
388
394{
395 if (!HasBit(rs->status, RSSFB_BASE_ENTRY)) return;
396
397 /* The tile 'before' the road stop must not be part of this 'line' */
398 assert(IsDriveThroughStopTile(rs->xy));
400
401 Entry temp;
402 temp.Rebuild(rs, rs->east == this);
403 if (temp.length != this->length || temp.occupied != this->occupied) NOT_REACHED();
404}
debug_inline constexpr 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.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
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:554
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.
bool HasTileAnyRoadType(Tile t, RoadTypes rts)
Check if a tile has one of the specified road types.
Definition road_map.h:222
Vehicle * FindVehiclesInRoadStop(Vehicle *v, void *data)
Add road vehicles to the station's list if needed.
Definition roadstop.cpp:326
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.
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?
bool IsDriveThroughStopTile(Tile t)
Is tile t a drive through road stop station or waypoint?
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.
RoadStopType GetRoadStopType(Tile t)
Get the road stop type of this tile.
Definition station_map.h:56
RoadStopType
Types of RoadStops.
Definition of base types and functions in a cross-platform compatible way.
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
VehicleType type
Type of vehicle.
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.
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.
int length
The length of the stop in tile 'units'.
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 CheckIntegrity(const RoadStop *rs) const
Check the integrity of the data in this struct.
Definition roadstop.cpp:393
void Rebuild(const RoadStop *rs, int side=-1)
Rebuild, from scratch, the vehicles and other metadata on this stop.
Definition roadstop.cpp:365
int occupied
The amount of occupied stop in tile 'units'.
A Stop for a Road Vehicle.
void SetEntranceBusy(bool busy)
Makes an entrance occupied or free.
Entry * east
The vehicles that entered from the east.
void Leave(RoadVehicle *rv)
Leave the road stop.
Definition roadstop.cpp:214
RoadStop * next
Next stop of the given type at this station.
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
@ RSSFB_BASE_ENTRY
Non-zero when the entries on this road stop are the primary, i.e. the ones to delete.
bool HasFreeBay() const
Checks whether there is a free bay in this road stop.
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.
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,.
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.
const Entry * GetEntry(DiagDirection dir) const
Get the drive through road stop entry struct for the given direction.
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 T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
Station data structure.
Vehicle data structure.
Direction direction
facing
bool HasArticulatedPart() const
Check if an engine has an articulated part.
VehStates vehstatus
Status.
virtual bool IsPrimaryVehicle() const
Whether this is the primary vehicle in the chain.
TileIndex tile
Current tile index.
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:502
@ Crashed
Vehicle is crashed.
Functions related to vehicles.
@ VEH_ROAD
Road vehicle type.