OpenTTD Source 20250522-master-g467f832c2f
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"
14#include "roadstop_base.h"
15#include "station_base.h"
16#include "vehicle_func.h"
17
18#include "safeguards.h"
19
23
24
28{
29 /* When we are the head we need to free the entries */
30 if (this->status.Test(RoadStopStatusFlag::BaseEntry)) {
31 delete this->entries;
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->entries == 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 uint16_t added = 1;
82 if (north && rs_north->entries != nullptr) {
83 /* There is a more northern one, so this can join them */
84 this->entries = rs_north->entries;
85
86 if (south && rs_south->entries != nullptr) {
87 /* There more southern tiles too, they must 'join' us too */
89 this->entries->east.occupied += rs_south->entries->east.occupied;
90 this->entries->west.occupied += rs_south->entries->west.occupied;
91
92 /* Free the now unneeded entries struct */
93 delete rs_south->entries;
94
95 /* Make all 'children' of the southern tile take the new master */
96 for (; IsDriveThroughRoadStopContinuation(this->xy, south_tile); south_tile += offset) {
97 rs_south = RoadStop::GetByTile(south_tile, rst);
98 if (rs_south->entries == nullptr) break;
99 rs_south->entries = rs_north->entries;
100 added++;
101 }
102 }
103 } else if (south && rs_south->entries != nullptr) {
104 /* There is one to the south, but not to the north... so we become 'parent' */
105 this->entries = rs_south->entries;
108 } else {
109 /* We are the only... so we are automatically the master */
110 this->entries = new Entries();
112 }
113
114 /* Now update the lengths */
115 added *= TILE_SIZE;
116 this->entries->east.length += added;
117 this->entries->west.length += added;
118}
119
125{
126 assert(this->entries != nullptr);
127
128 RoadStopType rst = GetRoadStopType(this->xy);
129 Axis axis = GetDriveThroughStopAxis(this->xy);
130 TileIndexDiff offset = TileOffsByAxis(axis);
131
132 /* Information about the tile north of us */
133 TileIndex north_tile = this->xy - offset;
134 bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
135 RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : nullptr;
136
137 /* Information about the tile south of us */
138 TileIndex south_tile = this->xy + offset;
139 bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
140 RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : nullptr;
141
142 /* Must only be cleared after we determined which neighbours are
143 * part of our little entry 'queue' */
144 DoClearSquare(this->xy);
145
146 if (north) {
147 /* There is a tile to the north, so we can't clear ourselves. */
148 if (south) {
149 /* There are more southern tiles too, they must be split;
150 * first make the new southern 'base' */
152 rs_south->entries = new Entries();
153
154 /* Keep track of the base because we need it later on */
155 RoadStop *rs_south_base = rs_south;
156 TileIndex base_tile = south_tile;
157
158 /* Make all (even more) southern stops part of the new entry queue */
159 for (south_tile += offset; IsDriveThroughRoadStopContinuation(base_tile, south_tile); south_tile += offset) {
160 rs_south = RoadStop::GetByTile(south_tile, rst);
161 rs_south->entries = rs_south_base->entries;
162 }
163
164 /* Find the other end; the northern most tile */
165 for (; IsDriveThroughRoadStopContinuation(base_tile, north_tile); north_tile -= offset) {
166 rs_north = RoadStop::GetByTile(north_tile, rst);
167 }
168
169 /* We have to rebuild the entries because we cannot easily determine
170 * how full each part is. So instead of keeping and maintaining a list
171 * of vehicles and using that to 'rebuild' the occupied state we just
172 * rebuild it from scratch as that removes lots of maintenance code
173 * for the vehicle list and it's faster in real games as long as you
174 * do not keep split and merge road stop every tick by the millions. */
175 rs_south_base->entries->east.Rebuild(rs_south_base);
176 rs_south_base->entries->west.Rebuild(rs_south_base);
177
178 assert(rs_north->status.Test(RoadStopStatusFlag::BaseEntry));
179 rs_north->entries->east.Rebuild(rs_north);
180 rs_north->entries->west.Rebuild(rs_north);
181 } else {
182 /* Only we left, so simple update the length. */
183 rs_north->entries->east.length -= TILE_SIZE;
184 rs_north->entries->west.length -= TILE_SIZE;
185 }
186 } else if (south) {
187 /* There is only something to the south. Hand over the base entry */
189 rs_south->entries->east.length -= TILE_SIZE;
190 rs_south->entries->west.length -= TILE_SIZE;
191 } else {
192 /* We were the last */
193 delete this->entries;
194 }
195
196 /* Make sure we don't get used for something 'incorrect' */
198 this->entries = nullptr;
199}
200
206{
207 if (IsBayRoadStopTile(rv->tile)) {
208 /* Vehicle is leaving a road stop tile, mark bay as free */
210 this->SetEntranceBusy(false);
211 } else {
212 /* Otherwise just leave the drive through's entry cache. */
213 this->GetEntry(DirToDiagDir(rv->direction)).Leave(rv);
214 }
215}
216
223{
224 if (IsBayRoadStopTile(this->xy)) {
225 /* For normal (non drive-through) road stops
226 * Check if station is busy or if there are no free bays or whether it is a articulated vehicle. */
227 if (this->IsEntranceBusy() || !this->HasFreeBay() || rv->HasArticulatedPart()) return false;
228
230
231 /* Allocate a bay and update the road state */
232 uint bay_nr = this->AllocateBay();
233 SB(rv->state, RVS_USING_SECOND_BAY, 1, bay_nr);
234
235 /* Mark the station entrance as busy */
236 this->SetEntranceBusy(true);
237 return true;
238 }
239
240 /* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */
241 this->GetEntry(DirToDiagDir(rv->direction)).Enter(rv);
242
243 /* Indicate a drive-through stop */
245 return true;
246}
247
256{
257 const Station *st = Station::GetByTile(tile);
258
259 for (RoadStop *rs = st->GetPrimaryRoadStop(type);; rs = rs->next) {
260 if (rs->xy == tile) return rs;
261 assert(rs->next != nullptr);
262 }
263}
264
270{
271 assert(this->occupied >= rv->gcache.cached_total_length);
273}
274
280{
281 /* we cannot assert on this->occupied < this->length because of the
282 * remote possibility that RVs are running through each other when
283 * trying to prevention an infinite jam. */
284 this->occupied += rv->gcache.cached_total_length;
285}
286
302
309static DiagDirection GetEntryDirection(bool east, Axis axis)
310{
311 switch (axis) {
312 case AXIS_X: return east ? DIAGDIR_NE : DIAGDIR_SW;
313 case AXIS_Y: return east ? DIAGDIR_SE : DIAGDIR_NW;
314 default: NOT_REACHED();
315 }
316}
317
323void RoadStop::Entry::Rebuild(const RoadStop *rs, int side)
324{
326
327 Axis axis = GetDriveThroughStopAxis(rs->xy);
328 if (side == -1) side = (&rs->entries->east == this);
329
330 auto entry_dir = GetEntryDirection(side, axis);
331 std::vector<const RoadVehicle *> vehicles;
332
333 this->length = 0;
334 TileIndexDiff offset = TileOffsByAxis(axis);
335 for (TileIndex tile = rs->xy; IsDriveThroughRoadStopContinuation(rs->xy, tile); tile += offset) {
336 this->length += TILE_SIZE;
337 for (const Vehicle *v : VehiclesOnTile(tile)) {
338 /* Not a RV or not in the right direction or crashed :( */
339 if (v->type != VEH_ROAD || DirToDiagDir(v->direction) != entry_dir || !v->IsPrimaryVehicle() || v->vehstatus.Test(VehState::Crashed)) continue;
340
341 const RoadVehicle *rv = RoadVehicle::From(v);
342 /* Don't add ones not in a road stop */
343 if (rv->state < RVSB_IN_ROAD_STOP) continue;
344
345 include(vehicles, rv);
346 }
347 }
348
349 this->occupied = 0;
350 for (const auto &it : vehicles) {
351 this->occupied += it->gcache.cached_total_length;
352 }
353}
354
355
361{
362 if (!rs->status.Test(RoadStopStatusFlag::BaseEntry)) return;
363
364 /* The tile 'before' the road stop must not be part of this 'line' */
365 assert(IsDriveThroughStopTile(rs->xy));
367
368 Entry temp;
369 temp.Rebuild(rs, &rs->entries->east == this);
370 if (temp.length != this->length || temp.occupied != this->occupied) NOT_REACHED();
371}
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 bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Set()
Set all bits.
Iterate over all vehicles on a tile.
Some simple functions to help with accessing containers.
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
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:206
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:309
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.
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.
Container for both east and west entry points.
Entry west
Information for vehicles that entered from the west.
Entry east
Information for vehicles that entered from the east.
Container for each entry point of a drive through road stop.
uint16_t occupied
The amount of occupied stop in tile 'units'.
void Leave(const RoadVehicle *rv)
Leave the road stop.
Definition roadstop.cpp:269
void Enter(const RoadVehicle *rv)
Enter the road stop.
Definition roadstop.cpp:279
void CheckIntegrity(const RoadStop *rs) const
Check the integrity of the data in this struct.
Definition roadstop.cpp:360
void Rebuild(const RoadStop *rs, int side=-1)
Rebuild, from scratch, the vehicles and other metadata on this stop.
Definition roadstop.cpp:323
uint16_t length
The length of the stop in tile 'units'.
A Stop for a Road Vehicle.
void SetEntranceBusy(bool busy)
Makes an entrance occupied or free.
@ BaseEntry
Non-zero when the entries on this road stop are the primary, i.e. the ones to delete.
void Leave(RoadVehicle *rv)
Leave the road stop.
Definition roadstop.cpp:205
RoadStop * next
Next stop of the given type at this station.
Entries * entries
Information about available and allocated bays.
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.
bool Enter(RoadVehicle *rv)
Enter the road stop.
Definition roadstop.cpp:222
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.
RoadStopStatusFlags status
Current status of the Stop. Access using *Bay and *Busy functions.
const Entry & GetEntry(DiagDirection dir) const
Get the drive through road stop entry struct for the given direction.
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:294
static RoadStop * GetByTile(TileIndex tile, RoadStopType type)
Find a roadstop at given tile.
Definition roadstop.cpp:255
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
void ClearDriveThrough()
Prepare for removal of this stop; update other neighbouring stops if needed.
Definition roadstop.cpp:124
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.
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
@ Crashed
Vehicle is crashed.
Functions related to vehicles.
@ VEH_ROAD
Road vehicle type.