OpenTTD Source: src/roadstop.cpp Source File

OpenTTD Source 14.1

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
20 RoadStopPool _roadstop_pool("RoadStop");
21 INSTANTIATE_POOL_METHODS(RoadStop)
22
23
26 RoadStop::~RoadStop()
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
42 RoadStop *RoadStop::GetNextRoadStop(const RoadVehicle *v) const
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
62 void RoadStop::MakeDriveThrough()
63 {
64 assert(this->east == nullptr && this->west == nullptr);
65
66 RoadStopType rst = GetRoadStopType(this->xy);
67 DiagDirection dir = GetRoadStopDir(this->xy);
68 /* Use absolute so we always go towards the northern tile */
69 TileIndexDiff offset = abs(TileOffsByDiagDir(dir));
70
71 /* Information about the tile north of us */
72 TileIndex north_tile = this->xy - offset;
73 bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
74 RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : nullptr;
75
76 /* Information about the tile south of us */
77 TileIndex south_tile = this->xy + offset;
78 bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
79 RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : nullptr;
80
81 /* Amount of road stops that will be added to the 'northern' head */
82 int added = 1;
83 if (north && rs_north->east != nullptr) { // (east != nullptr) == (west != nullptr)
84 /* There is a more northern one, so this can join them */
85 this->east = rs_north->east;
86 this->west = rs_north->west;
87
88 if (south && rs_south->east != nullptr) { // (east != nullptr) == (west != nullptr)
89 /* There more southern tiles too, they must 'join' us too */
90 ClrBit(rs_south->status, RSSFB_BASE_ENTRY);
91 this->east->occupied += rs_south->east->occupied;
92 this->west->occupied += rs_south->west->occupied;
93
94 /* Free the now unneeded entry structs */
95 delete rs_south->east;
96 delete rs_south->west;
97
98 /* Make all 'children' of the southern tile take the new master */
99 for (; IsDriveThroughRoadStopContinuation(this->xy, south_tile); south_tile += offset) {
100 rs_south = RoadStop::GetByTile(south_tile, rst);
101 if (rs_south->east == nullptr) break;
102 rs_south->east = rs_north->east;
103 rs_south->west = rs_north->west;
104 added++;
105 }
106 }
107 } else if (south && rs_south->east != nullptr) { // (east != nullptr) == (west != nullptr)
108 /* There is one to the south, but not to the north... so we become 'parent' */
109 this->east = rs_south->east;
110 this->west = rs_south->west;
111 SetBit(this->status, RSSFB_BASE_ENTRY);
112 ClrBit(rs_south->status, RSSFB_BASE_ENTRY);
113 } else {
114 /* We are the only... so we are automatically the master */
115 this->east = new Entry();
116 this->west = new Entry();
117 SetBit(this->status, RSSFB_BASE_ENTRY);
118 }
119
120 /* Now update the lengths */
121 added *= TILE_SIZE;
122 this->east->length += added;
123 this->west->length += added;
124 }
125
130 void RoadStop::ClearDriveThrough()
131 {
132 assert(this->east != nullptr && this->west != nullptr);
133
134 RoadStopType rst = GetRoadStopType(this->xy);
135 DiagDirection dir = GetRoadStopDir(this->xy);
136 /* Use absolute so we always go towards the northern tile */
137 TileIndexDiff offset = abs(TileOffsByDiagDir(dir));
138
139 /* Information about the tile north of us */
140 TileIndex north_tile = this->xy - offset;
141 bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
142 RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : nullptr;
143
144 /* Information about the tile south of us */
145 TileIndex south_tile = this->xy + offset;
146 bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
147 RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : nullptr;
148
149 /* Must only be cleared after we determined which neighbours are
150 * part of our little entry 'queue' */
151 DoClearSquare(this->xy);
152
153 if (north) {
154 /* There is a tile to the north, so we can't clear ourselves. */
155 if (south) {
156 /* There are more southern tiles too, they must be split;
157 * first make the new southern 'base' */
158 SetBit(rs_south->status, RSSFB_BASE_ENTRY);
159 rs_south->east = new Entry();
160 rs_south->west = new Entry();
161
162 /* Keep track of the base because we need it later on */
163 RoadStop *rs_south_base = rs_south;
164 TileIndex base_tile = south_tile;
165
166 /* Make all (even more) southern stops part of the new entry queue */
167 for (south_tile += offset; IsDriveThroughRoadStopContinuation(base_tile, south_tile); south_tile += offset) {
168 rs_south = RoadStop::GetByTile(south_tile, rst);
169 rs_south->east = rs_south_base->east;
170 rs_south->west = rs_south_base->west;
171 }
172
173 /* Find the other end; the northern most tile */
174 for (; IsDriveThroughRoadStopContinuation(base_tile, north_tile); north_tile -= offset) {
175 rs_north = RoadStop::GetByTile(north_tile, rst);
176 }
177
178 /* We have to rebuild the entries because we cannot easily determine
179 * how full each part is. So instead of keeping and maintaining a list
180 * of vehicles and using that to 'rebuild' the occupied state we just
181 * rebuild it from scratch as that removes lots of maintenance code
182 * for the vehicle list and it's faster in real games as long as you
183 * do not keep split and merge road stop every tick by the millions. */
184 rs_south_base->east->Rebuild(rs_south_base);
185 rs_south_base->west->Rebuild(rs_south_base);
186
187 assert(HasBit(rs_north->status, RSSFB_BASE_ENTRY));
188 rs_north->east->Rebuild(rs_north);
189 rs_north->west->Rebuild(rs_north);
190 } else {
191 /* Only we left, so simple update the length. */
192 rs_north->east->length -= TILE_SIZE;
193 rs_north->west->length -= TILE_SIZE;
194 }
195 } else if (south) {
196 /* There is only something to the south. Hand over the base entry */
197 SetBit(rs_south->status, RSSFB_BASE_ENTRY);
198 rs_south->east->length -= TILE_SIZE;
199 rs_south->west->length -= TILE_SIZE;
200 } else {
201 /* We were the last */
202 delete this->east;
203 delete this->west;
204 }
205
206 /* Make sure we don't get used for something 'incorrect' */
207 ClrBit(this->status, RSSFB_BASE_ENTRY);
208 this->east = nullptr;
209 this->west = nullptr;
210 }
211
216 void RoadStop::Leave(RoadVehicle *rv)
217 {
218 if (IsBayRoadStopTile(rv->tile)) {
219 /* Vehicle is leaving a road stop tile, mark bay as free */
220 this->FreeBay(HasBit(rv->state, RVS_USING_SECOND_BAY));
221 this->SetEntranceBusy(false);
222 } else {
223 /* Otherwise just leave the drive through's entry cache. */
224 this->GetEntry(DirToDiagDir(rv->direction))->Leave(rv);
225 }
226 }
227
233 bool RoadStop::Enter(RoadVehicle *rv)
234 {
235 if (IsBayRoadStopTile(this->xy)) {
236 /* For normal (non drive-through) road stops
237 * Check if station is busy or if there are no free bays or whether it is a articulated vehicle. */
238 if (this->IsEntranceBusy() || !this->HasFreeBay() || rv->HasArticulatedPart()) return false;
239
240 SetBit(rv->state, RVS_IN_ROAD_STOP);
241
242 /* Allocate a bay and update the road state */
243 uint bay_nr = this->AllocateBay();
244 SB(rv->state, RVS_USING_SECOND_BAY, 1, bay_nr);
245
246 /* Mark the station entrance as busy */
247 this->SetEntranceBusy(true);
248 return true;
249 }
250
251 /* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */
252 this->GetEntry(DirToDiagDir(rv->direction))->Enter(rv);
253
254 /* Indicate a drive-through stop */
255 SetBit(rv->state, RVS_IN_DT_ROAD_STOP);
256 return true;
257 }
258
266 /* static */ RoadStop *RoadStop::GetByTile(TileIndex tile, RoadStopType type)
267 {
268 const Station *st = Station::GetByTile(tile);
269
270 for (RoadStop *rs = st->GetPrimaryRoadStop(type);; rs = rs->next) {
271 if (rs->xy == tile) return rs;
272 assert(rs->next != nullptr);
273 }
274 }
275
280 void RoadStop::Entry::Leave(const RoadVehicle *rv)
281 {
282 this->occupied -= rv->gcache.cached_total_length;
283 assert(this->occupied >= 0);
284 }
285
290 void RoadStop::Entry::Enter(const RoadVehicle *rv)
291 {
292 /* we cannot assert on this->occupied < this->length because of the
293 * remote possibility that RVs are running through each other when
294 * trying to prevention an infinite jam. */
295 this->occupied += rv->gcache.cached_total_length;
296 }
297
305 /* static */ bool RoadStop::IsDriveThroughRoadStopContinuation(TileIndex rs, TileIndex next)
306 {
307 return IsTileType(next, MP_STATION) &&
308 GetStationIndex(next) == GetStationIndex(rs) &&
309 GetStationType(next) == GetStationType(rs) &&
310 GetRoadStopDir(next) == GetRoadStopDir(rs) &&
311 IsDriveThroughStopTile(next);
312 }
313
314 typedef std::list<const RoadVehicle *> RVList;
315
317 struct RoadStopEntryRebuilderHelper {
318 RVList vehicles;
319 DiagDirection dir;
320 };
321
328 Vehicle *FindVehiclesInRoadStop(Vehicle *v, void *data)
329 {
330 RoadStopEntryRebuilderHelper *rserh = (RoadStopEntryRebuilderHelper*)data;
331 /* Not a RV or not in the right direction or crashed :( */
332 if (v->type != VEH_ROAD || DirToDiagDir(v->direction) != rserh->dir || !v->IsPrimaryVehicle() || (v->vehstatus & VS_CRASHED) != 0) return nullptr;
333
334 RoadVehicle *rv = RoadVehicle::From(v);
335 /* Don't add ones not in a road stop */
336 if (rv->state < RVSB_IN_ROAD_STOP) return nullptr;
337
338 /* Do not add duplicates! */
339 for (const auto &it : rserh->vehicles) {
340 if (rv == it) return nullptr;
341 }
342
343 rserh->vehicles.push_back(rv);
344 return nullptr;
345 }
346
352 void RoadStop::Entry::Rebuild(const RoadStop *rs, int side)
353 {
354 assert(HasBit(rs->status, RSSFB_BASE_ENTRY));
355
356 DiagDirection dir = GetRoadStopDir(rs->xy);
357 if (side == -1) side = (rs->east == this);
358
359 RoadStopEntryRebuilderHelper rserh;
360 rserh.dir = side ? dir : ReverseDiagDir(dir);
361
362 this->length = 0;
363 TileIndexDiff offset = abs(TileOffsByDiagDir(dir));
364 for (TileIndex tile = rs->xy; IsDriveThroughRoadStopContinuation(rs->xy, tile); tile += offset) {
365 this->length += TILE_SIZE;
366 FindVehicleOnPos(tile, &rserh, FindVehiclesInRoadStop);
367 }
368
369 this->occupied = 0;
370 for (const auto &it : rserh.vehicles) {
371 this->occupied += it->gcache.cached_total_length;
372 }
373 }
374
375
380 void RoadStop::Entry::CheckIntegrity(const RoadStop *rs) const
381 {
382 if (!HasBit(rs->status, RSSFB_BASE_ENTRY)) return;
383
384 /* The tile 'before' the road stop must not be part of this 'line' */
385 assert(!IsDriveThroughRoadStopContinuation(rs->xy, rs->xy - abs(TileOffsByDiagDir(GetRoadStopDir(rs->xy)))));
386
387 Entry temp;
388 temp.Rebuild(rs, rs->east == this);
389 if (temp.length != this->length || temp.occupied != this->occupied) NOT_REACHED();
390 }
RoadVehicle
Buses, trucks and trams belong to this class.
Definition: roadveh.h:106
RoadVehicle::state
byte state
Definition: roadveh.h:108
SetBit
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
Definition: bitmath_func.hpp:121
RoadStopEntryRebuilderHelper::dir
DiagDirection dir
The direction the vehicle has to face to be added.
Definition: roadstop.cpp:319
RoadStopType
RoadStopType
Types of RoadStops.
Definition: station_type.h:43
RoadStop::HasFreeBay
bool HasFreeBay() const
Checks whether there is a free bay in this road stop.
Definition: roadstop_base.h:83
RoadStop::Entry::CheckIntegrity
void CheckIntegrity(const RoadStop *rs) const
Check the integrity of the data in this struct.
Definition: roadstop.cpp:380
RoadStop::IsEntranceBusy
bool IsEntranceBusy() const
Checks whether the entrance of the road stop is occupied by a vehicle.
Definition: roadstop_base.h:103
RoadStop::xy
TileIndex xy
Position on the map.
Definition: roadstop_base.h:67
Station
Station data structure.
Definition: station_base.h:442
Vehicle::vehstatus
byte vehstatus
Status.
Definition: vehicle_base.h:349
RVSB_IN_ROAD_STOP
@ RVSB_IN_ROAD_STOP
The vehicle is in a road stop.
Definition: roadveh.h:49
RoadStop::GetByTile
static RoadStop * GetByTile(TileIndex tile, RoadStopType type)
Find a roadstop at given tile.
Definition: roadstop.cpp:266
TILE_SIZE
static const uint TILE_SIZE
Tile size in world coordinates.
Definition: tile_type.h:15
RoadStop::GetEntry
const Entry * GetEntry(DiagDirection dir) const
Get the drive through road stop entry struct for the given direction.
Definition: roadstop_base.h:122
GetRoadStopDir
DiagDirection GetRoadStopDir(Tile t)
Gets the direction the road stop entrance points towards.
Definition: station_map.h:258
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > >
_roadstop_pool
RoadStopPool _roadstop_pool("RoadStop")
The pool of roadstops.
RoadStop::Enter
bool Enter(RoadVehicle *rv)
Enter the road stop.
Definition: roadstop.cpp:233
VEH_ROAD
@ VEH_ROAD
Road vehicle type.
Definition: vehicle_type.h:25
Vehicle
Vehicle data structure.
Definition: vehicle_base.h:240
Vehicle::IsPrimaryVehicle
virtual bool IsPrimaryVehicle() const
Whether this is the primary vehicle in the chain.
Definition: vehicle_base.h:473
RVS_USING_SECOND_BAY
@ RVS_USING_SECOND_BAY
Only used while in a road stop.
Definition: roadveh.h:42
RVList
std::list< const RoadVehicle * > RVList
A list of road vehicles.
Definition: roadstop.cpp:314
RoadStop::Entry::length
int length
The length of the stop in tile 'units'.
Definition: roadstop_base.h:34
RoadStop::Entry::Enter
void Enter(const RoadVehicle *rv)
Enter the road stop.
Definition: roadstop.cpp:290
RoadStop::Entry::Rebuild
void Rebuild(const RoadStop *rs, int side=-1)
Rebuild, from scratch, the vehicles and other metadata on this stop.
Definition: roadstop.cpp:352
RoadStop::MakeDriveThrough
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
RoadStop::RSSFB_BASE_ENTRY
@ 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:27
RoadStop::west
Entry * west
The vehicles that entered from the west.
Definition: roadstop_base.h:151
RoadStop::AllocateBay
uint AllocateBay()
Allocates a bay.
Definition: roadstop_base.h:158
RoadStop::next
struct RoadStop * next
Next stop of the given type at this station.
Definition: roadstop_base.h:69
RoadStop::Entry::occupied
int occupied
The amount of occupied stop in tile 'units'.
Definition: roadstop_base.h:35
IsBayRoadStopTile
bool IsBayRoadStopTile(Tile t)
Is tile t a bay (non-drive through) road stop station?
Definition: station_map.h:223
roadstop_base.h
Vehicle::tile
TileIndex tile
Current tile index.
Definition: vehicle_base.h:260
VS_CRASHED
@ VS_CRASHED
Vehicle is crashed.
Definition: vehicle_base.h:40
RoadStopEntryRebuilderHelper::vehicles
RVList vehicles
The list of vehicles to possibly add to.
Definition: roadstop.cpp:318
RoadStop::FreeBay
void FreeBay(uint nr)
Frees the given bay.
Definition: roadstop_base.h:184
ReverseDiagDir
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
Definition: direction_func.h:118
GroundVehicle::gcache
GroundVehicleCache gcache
Cache of often calculated values.
Definition: ground_vehicle.hpp:80
safeguards.h
DirToDiagDir
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
Definition: direction_func.h:166
RVS_IN_ROAD_STOP
@ RVS_IN_ROAD_STOP
The vehicle is in a road stop.
Definition: roadveh.h:45
RoadVehicle::compatible_roadtypes
RoadTypes compatible_roadtypes
Roadtypes this consist is powered on.
Definition: roadveh.h:117
TileIndexDiff
int32_t TileIndexDiff
An offset value between two tiles.
Definition: map_func.h:376
DiagDirection
DiagDirection
Enumeration for diagonal directions.
Definition: direction_type.h:73
RoadStop::east
Entry * east
The vehicles that entered from the east.
Definition: roadstop_base.h:150
RoadStop::SetEntranceBusy
void SetEntranceBusy(bool busy)
Makes an entrance occupied or free.
Definition: roadstop_base.h:112
stdafx.h
RoadStop::Leave
void Leave(RoadVehicle *rv)
Leave the road stop.
Definition: roadstop.cpp:216
GetStationType
StationType GetStationType(Tile t)
Get the station type of this tile.
Definition: station_map.h:44
RoadStopEntryRebuilderHelper
Helper for finding RVs in a road stop.
Definition: roadstop.cpp:317
Vehicle::direction
Direction direction
facing
Definition: vehicle_base.h:303
TileOffsByDiagDir
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition: map_func.h:563
RoadStop::Entry::Leave
void Leave(const RoadVehicle *rv)
Leave the road stop.
Definition: roadstop.cpp:280
vehicle_func.h
station_base.h
Pool
Base class for all pools.
Definition: pool_type.hpp:80
RoadStop::ClearDriveThrough
void ClearDriveThrough()
Prepare for removal of this stop; update other neighbouring stops if needed.
Definition: roadstop.cpp:130
SpecializedVehicle< RoadVehicle, Type >::From
static RoadVehicle * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
Definition: vehicle_base.h:1211
abs
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition: math_func.hpp:23
GetStationIndex
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition: station_map.h:28
FindVehiclesInRoadStop
Vehicle * FindVehiclesInRoadStop(Vehicle *v, void *data)
Add road vehicles to the station's list if needed.
Definition: roadstop.cpp:328
RoadStop::IsDriveThroughRoadStopContinuation
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:305
MP_STATION
@ MP_STATION
A tile of a station.
Definition: tile_type.h:53
SpecializedStation< Station, false >::GetByTile
static Station * GetByTile(TileIndex tile)
Get the station belonging to a specific tile.
Definition: base_station_base.h:278
Vehicle::HasArticulatedPart
bool HasArticulatedPart() const
Check if an engine has an articulated part.
Definition: vehicle_base.h:959
FindVehicleOnPos
void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
Find a vehicle from a specific location.
Definition: vehicle.cpp:505
RoadStop::status
byte status
Current status of the Stop,.
Definition: roadstop_base.h:68
INSTANTIATE_POOL_METHODS
#define INSTANTIATE_POOL_METHODS(name)
Force instantiation of pool methods so we don't get linker errors.
Definition: pool_func.hpp:237
GetRoadStopType
RoadStopType GetRoadStopType(Tile t)
Get the road stop type of this tile.
Definition: station_map.h:56
RVS_IN_DT_ROAD_STOP
@ RVS_IN_DT_ROAD_STOP
The vehicle is in a drive-through road stop.
Definition: roadveh.h:46
SB
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.
Definition: bitmath_func.hpp:58
IsTileType
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition: tile_map.h:150
RoadStop
A Stop for a Road Vehicle.
Definition: roadstop_base.h:22
BaseVehicle::type
VehicleType type
Type of vehicle.
Definition: vehicle_type.h:51
pool_func.hpp
HasTileAnyRoadType
bool HasTileAnyRoadType(Tile t, RoadTypes rts)
Check if a tile has one of the specified road types.
Definition: road_map.h:222
RoadStop::Entry
Container for each entry point of a drive through road stop.
Definition: roadstop_base.h:32
ClrBit
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
Definition: bitmath_func.hpp:151
GroundVehicleCache::cached_total_length
uint16_t cached_total_length
Length of the whole vehicle (valid only for the first engine).
Definition: ground_vehicle.hpp:42
RoadStop::GetNextRoadStop
RoadStop * GetNextRoadStop(const struct RoadVehicle *v) const
Get the next road stop accessible by this vehicle.
Definition: roadstop.cpp:42
IsDriveThroughStopTile
bool IsDriveThroughStopTile(Tile t)
Is tile t a drive through road stop station?
Definition: station_map.h:233
roadveh.h
HasBit
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition: bitmath_func.hpp:103