OpenTTD Source 20251213-master-g1091fa6071
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
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
41{
42 for (RoadStop *rs = this->next; rs != nullptr; rs = rs->next) {
43 /* The vehicle cannot go to this roadstop (different roadtype) */
44 if (!HasTileAnyRoadType(rs->xy, v->compatible_roadtypes)) continue;
45 /* The vehicle is articulated and can therefore not go to a standard road stop. */
46 if (IsBayRoadStopTile(rs->xy) && v->HasArticulatedPart()) continue;
47
48 /* The vehicle can actually go to this road stop. So, return it! */
49 return rs;
50 }
51
52 return nullptr;
53}
54
61{
62 assert(this->entries == nullptr);
63
64 RoadStopType rst = GetRoadStopType(this->xy);
65 Axis axis = GetDriveThroughStopAxis(this->xy);
66 TileIndexDiff offset = TileOffsByAxis(axis);
67
68 /* Information about the tile north of us */
69 TileIndex north_tile = this->xy - offset;
70 bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
71 RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : nullptr;
72
73 /* Information about the tile south of us */
74 TileIndex south_tile = this->xy + offset;
75 bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
76 RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : nullptr;
77
78 /* Amount of road stops that will be added to the 'northern' head */
79 uint16_t added = 1;
80 if (north && rs_north->entries != nullptr) {
81 /* There is a more northern one, so this can join them */
82 this->entries = rs_north->entries;
83
84 if (south && rs_south->entries != nullptr) {
85 /* There more southern tiles too, they must 'join' us too */
87 this->entries->east.occupied += rs_south->entries->east.occupied;
88 this->entries->west.occupied += rs_south->entries->west.occupied;
89
90 /* Free the now unneeded entries struct */
91 delete rs_south->entries;
92
93 /* Make all 'children' of the southern tile take the new master */
94 for (; IsDriveThroughRoadStopContinuation(this->xy, south_tile); south_tile += offset) {
95 rs_south = RoadStop::GetByTile(south_tile, rst);
96 if (rs_south->entries == nullptr) break;
97 rs_south->entries = rs_north->entries;
98 added++;
99 }
100 }
101 } else if (south && rs_south->entries != nullptr) {
102 /* There is one to the south, but not to the north... so we become 'parent' */
103 this->entries = rs_south->entries;
106 } else {
107 /* We are the only... so we are automatically the master */
108 this->entries = new Entries();
110 }
111
112 /* Now update the lengths */
113 added *= TILE_SIZE;
114 this->entries->east.length += added;
115 this->entries->west.length += added;
116}
117
123{
124 assert(this->entries != nullptr);
125
126 RoadStopType rst = GetRoadStopType(this->xy);
127 Axis axis = GetDriveThroughStopAxis(this->xy);
128 TileIndexDiff offset = TileOffsByAxis(axis);
129
130 /* Information about the tile north of us */
131 TileIndex north_tile = this->xy - offset;
132 bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
133 RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : nullptr;
134
135 /* Information about the tile south of us */
136 TileIndex south_tile = this->xy + offset;
137 bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
138 RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : nullptr;
139
140 /* Must only be cleared after we determined which neighbours are
141 * part of our little entry 'queue' */
142 DoClearSquare(this->xy);
143
144 if (north) {
145 /* There is a tile to the north, so we can't clear ourselves. */
146 if (south) {
147 /* There are more southern tiles too, they must be split;
148 * first make the new southern 'base' */
150 rs_south->entries = new Entries();
151
152 /* Keep track of the base because we need it later on */
153 RoadStop *rs_south_base = rs_south;
154 TileIndex base_tile = south_tile;
155
156 /* Make all (even more) southern stops part of the new entry queue */
157 for (south_tile += offset; IsDriveThroughRoadStopContinuation(base_tile, south_tile); south_tile += offset) {
158 rs_south = RoadStop::GetByTile(south_tile, rst);
159 rs_south->entries = rs_south_base->entries;
160 }
161
162 /* Find the other end; the northern most tile */
163 for (; IsDriveThroughRoadStopContinuation(base_tile, north_tile); north_tile -= offset) {
164 rs_north = RoadStop::GetByTile(north_tile, rst);
165 }
166
167 /* We have to rebuild the entries because we cannot easily determine
168 * how full each part is. So instead of keeping and maintaining a list
169 * of vehicles and using that to 'rebuild' the occupied state we just
170 * rebuild it from scratch as that removes lots of maintenance code
171 * for the vehicle list and it's faster in real games as long as you
172 * do not keep split and merge road stop every tick by the millions. */
173 rs_south_base->entries->east.Rebuild(rs_south_base);
174 rs_south_base->entries->west.Rebuild(rs_south_base);
175
176 assert(rs_north->status.Test(RoadStopStatusFlag::BaseEntry));
177 rs_north->entries->east.Rebuild(rs_north);
178 rs_north->entries->west.Rebuild(rs_north);
179 } else {
180 /* Only we left, so simple update the length. */
181 rs_north->entries->east.length -= TILE_SIZE;
182 rs_north->entries->west.length -= TILE_SIZE;
183 }
184 } else if (south) {
185 /* There is only something to the south. Hand over the base entry */
187 rs_south->entries->east.length -= TILE_SIZE;
188 rs_south->entries->west.length -= TILE_SIZE;
189 } else {
190 /* We were the last */
191 delete this->entries;
192 }
193
194 /* Make sure we don't get used for something 'incorrect' */
196 this->entries = nullptr;
197}
198
204{
205 if (IsBayRoadStopTile(rv->tile)) {
206 /* Vehicle is leaving a road stop tile, mark bay as free */
208 this->SetEntranceBusy(false);
209 } else {
210 /* Otherwise just leave the drive through's entry cache. */
211 this->GetEntry(DirToDiagDir(rv->direction)).Leave(rv);
212 }
213}
214
221{
222 if (IsBayRoadStopTile(this->xy)) {
223 /* For normal (non drive-through) road stops
224 * Check if station is busy or if there are no free bays or whether it is a articulated vehicle. */
225 if (this->IsEntranceBusy() || !this->HasFreeBay() || rv->HasArticulatedPart()) return false;
226
228
229 /* Allocate a bay and update the road state */
230 uint bay_nr = this->AllocateBay();
231 SB(rv->state, RVS_USING_SECOND_BAY, 1, bay_nr);
232
233 /* Mark the station entrance as busy */
234 this->SetEntranceBusy(true);
235 return true;
236 }
237
238 /* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */
239 this->GetEntry(DirToDiagDir(rv->direction)).Enter(rv);
240
241 /* Indicate a drive-through stop */
243 return true;
244}
245
254{
255 const Station *st = Station::GetByTile(tile);
256
257 for (RoadStop *rs = st->GetPrimaryRoadStop(type);; rs = rs->next) {
258 if (rs->xy == tile) return rs;
259 assert(rs->next != nullptr);
260 }
261}
262
268{
269 assert(this->occupied >= rv->gcache.cached_total_length);
271}
272
278{
279 /* we cannot assert on this->occupied < this->length because of the
280 * remote possibility that RVs are running through each other when
281 * trying to prevention an infinite jam. */
282 this->occupied += rv->gcache.cached_total_length;
283}
284
300
307static DiagDirection GetEntryDirection(bool east, Axis axis)
308{
309 switch (axis) {
310 case AXIS_X: return east ? DIAGDIR_NE : DIAGDIR_SW;
311 case AXIS_Y: return east ? DIAGDIR_SE : DIAGDIR_NW;
312 default: NOT_REACHED();
313 }
314}
315
321void RoadStop::Entry::Rebuild(const RoadStop *rs, int side)
322{
324
325 Axis axis = GetDriveThroughStopAxis(rs->xy);
326 if (side == -1) side = (&rs->entries->east == this);
327
328 auto entry_dir = GetEntryDirection(side, axis);
329 std::vector<const RoadVehicle *> vehicles;
330
331 this->length = 0;
332 TileIndexDiff offset = TileOffsByAxis(axis);
333 for (TileIndex tile = rs->xy; IsDriveThroughRoadStopContinuation(rs->xy, tile); tile += offset) {
334 this->length += TILE_SIZE;
335 for (const Vehicle *v : VehiclesOnTile(tile)) {
336 /* Not a RV or not in the right direction or crashed :( */
337 if (v->type != VEH_ROAD || DirToDiagDir(v->direction) != entry_dir || !v->IsPrimaryVehicle() || v->vehstatus.Test(VehState::Crashed)) continue;
338
339 const RoadVehicle *rv = RoadVehicle::From(v);
340 /* Don't add ones not in a road stop */
341 if (rv->state < RVSB_IN_ROAD_STOP) continue;
342
343 include(vehicles, rv);
344 }
345 }
346
347 this->occupied = 0;
348 for (const auto &it : vehicles) {
349 this->occupied += it->gcache.cached_total_length;
350 }
351}
352
353
359{
360 if (!rs->status.Test(RoadStopStatusFlag::BaseEntry)) return;
361
362 /* The tile 'before' the road stop must not be part of this 'line' */
363 assert(IsDriveThroughStopTile(rs->xy));
365
366 Entry temp;
367 temp.Rebuild(rs, &rs->entries->east == this);
368 if (temp.length != this->length || temp.occupied != this->occupied) NOT_REACHED();
369}
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 HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
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:567
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:307
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:267
void Enter(const RoadVehicle *rv)
Enter the road stop.
Definition roadstop.cpp:277
void CheckIntegrity(const RoadStop *rs) const
Check the integrity of the data in this struct.
Definition roadstop.cpp:358
void Rebuild(const RoadStop *rs, int side=-1)
Rebuild, from scratch, the vehicles and other metadata on this stop.
Definition roadstop.cpp:321
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:203
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:40
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:220
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:292
static RoadStop * GetByTile(TileIndex tile, RoadStopType type)
Find a roadstop at given tile.
Definition roadstop.cpp:253
void MakeDriveThrough()
Join this road stop to another 'base' road stop if possible; fill all necessary data to become an act...
Definition roadstop.cpp:60
void ClearDriveThrough()
Prepare for removal of this stop; update other neighbouring stops if needed.
Definition roadstop.cpp:122
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 bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
static constexpr 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.