OpenTTD Source: src/roadstop.cpp Source File

OpenTTD Source 15.0-RC2

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"
13#include "core/container_func.hpp"
14#include "roadstop_base.h"
15#include "station_base.h"
16#include "vehicle_func.h"
17
18#include "safeguards.h"
19
21 RoadStopPool _roadstop_pool("RoadStop");
22 INSTANTIATE_POOL_METHODS(RoadStop)
23
24
27 RoadStop::~RoadStop()
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
40 RoadStop *RoadStop::GetNextRoadStop(const RoadVehicle *v) const
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
60 void RoadStop::MakeDriveThrough()
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 */
86 rs_south->status.Reset(RoadStopStatusFlag::BaseEntry);
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;
104 this->status.Set(RoadStopStatusFlag::BaseEntry);
105 rs_south->status.Reset(RoadStopStatusFlag::BaseEntry);
106 } else {
107 /* We are the only... so we are automatically the master */
108 this->entries = new Entries();
109 this->status.Set(RoadStopStatusFlag::BaseEntry);
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
122 void RoadStop::ClearDriveThrough()
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' */
149 rs_south->status.Set(RoadStopStatusFlag::BaseEntry);
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 */
186 rs_south->status.Set(RoadStopStatusFlag::BaseEntry);
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' */
195 this->status.Reset(RoadStopStatusFlag::BaseEntry);
196 this->entries = nullptr;
197}
198
203 void RoadStop::Leave(RoadVehicle *rv)
204{
205 if (IsBayRoadStopTile(rv->tile)) {
206 /* Vehicle is leaving a road stop tile, mark bay as free */
207 this->FreeBay(HasBit(rv->state, RVS_USING_SECOND_BAY));
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
220 bool RoadStop::Enter(RoadVehicle *rv)
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
227 SetBit(rv->state, RVS_IN_ROAD_STOP);
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 */
242 SetBit(rv->state, RVS_IN_DT_ROAD_STOP);
243 return true;
244}
245
253/* static */ RoadStop *RoadStop::GetByTile(TileIndex tile, RoadStopType type)
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
267 void RoadStop::Entry::Leave(const RoadVehicle *rv)
268{
269 assert(this->occupied >= rv->gcache.cached_total_length);
270 this->occupied -= rv->gcache.cached_total_length;
271}
272
277 void RoadStop::Entry::Enter(const RoadVehicle *rv)
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
292/* static */ bool RoadStop::IsDriveThroughRoadStopContinuation(TileIndex rs, TileIndex next)
293{
294 return IsTileType(next, MP_STATION) &&
295 GetStationIndex(next) == GetStationIndex(rs) &&
296 GetStationType(next) == GetStationType(rs) &&
297 IsDriveThroughStopTile(next) &&
298 GetDriveThroughStopAxis(next) == GetDriveThroughStopAxis(rs);
299}
300
307 static 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
321 void RoadStop::Entry::Rebuild(const RoadStop *rs, int side)
322{
323 assert(rs->status.Test(RoadStopStatusFlag::BaseEntry));
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
358 void RoadStop::Entry::CheckIntegrity(const RoadStop *rs) const
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));
364 assert(!IsDriveThroughRoadStopContinuation(rs->xy, rs->xy - TileOffsByAxis(GetDriveThroughStopAxis(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}
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
SetBit
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
Definition bitmath_func.hpp:121
HasBit
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition bitmath_func.hpp:103
BaseBitSet::Test
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
Definition base_bitset_type.hpp:141
BaseBitSet::Reset
constexpr Timpl & Reset()
Reset all bits.
Definition base_bitset_type.hpp:83
BaseBitSet::Set
constexpr Timpl & Set()
Set all bits.
Definition base_bitset_type.hpp:40
VehiclesOnTile
Iterate over all vehicles on a tile.
Definition vehicle_func.h:53
container_func.hpp
Some simple functions to help with accessing containers.
include
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
Definition container_func.hpp:24
DirToDiagDir
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
Definition direction_func.h:166
Axis
Axis
Allow incrementing of DiagDirDiff variables.
Definition direction_type.h:123
AXIS_X
@ AXIS_X
The X axis.
Definition direction_type.h:124
AXIS_Y
@ AXIS_Y
The y axis.
Definition direction_type.h:125
DiagDirection
DiagDirection
Enumeration for diagonal directions.
Definition direction_type.h:78
DIAGDIR_NE
@ DIAGDIR_NE
Northeast, upper right on your monitor.
Definition direction_type.h:80
DIAGDIR_NW
@ DIAGDIR_NW
Northwest.
Definition direction_type.h:83
DIAGDIR_SE
@ DIAGDIR_SE
Southeast.
Definition direction_type.h:81
DIAGDIR_SW
@ DIAGDIR_SW
Southwest.
Definition direction_type.h:82
TileOffsByAxis
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:567
TileIndexDiff
int32_t TileIndexDiff
An offset value between two tiles.
Definition map_type.h:23
pool_func.hpp
Some methods of Pool are placed here in order to reduce compilation time and binary size.
INSTANTIATE_POOL_METHODS
#define INSTANTIATE_POOL_METHODS(name)
Force instantiation of pool methods so we don't get linker errors.
Definition pool_func.hpp:210
HasTileAnyRoadType
bool HasTileAnyRoadType(Tile t, RoadTypes rts)
Check if a tile has one of the specified road types.
Definition road_map.h:206
GetEntryDirection
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
_roadstop_pool
RoadStopPool _roadstop_pool("RoadStop")
The pool of roadstops.
roadstop_base.h
Base class for roadstops.
roadveh.h
Road vehicle states.
RVSB_IN_ROAD_STOP
@ RVSB_IN_ROAD_STOP
The vehicle is in a road stop.
Definition roadveh.h:49
RVS_USING_SECOND_BAY
@ RVS_USING_SECOND_BAY
Only used while in a road stop.
Definition roadveh.h:42
RVS_IN_DT_ROAD_STOP
@ RVS_IN_DT_ROAD_STOP
The vehicle is in a drive-through road stop.
Definition roadveh.h:46
RVS_IN_ROAD_STOP
@ RVS_IN_ROAD_STOP
The vehicle is in a road stop.
Definition roadveh.h:45
safeguards.h
A number of safeguards to prevent using unsafe methods.
station_base.h
Base classes/functions for stations.
GetStationType
StationType GetStationType(Tile t)
Get the station type of this tile.
Definition station_map.h:44
IsBayRoadStopTile
bool IsBayRoadStopTile(Tile t)
Is tile t a bay (non-drive through) road stop station?
Definition station_map.h:266
IsDriveThroughStopTile
bool IsDriveThroughStopTile(Tile t)
Is tile t a drive through road stop station or waypoint?
Definition station_map.h:276
GetStationIndex
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
GetDriveThroughStopAxis
Axis GetDriveThroughStopAxis(Tile t)
Gets the axis of the drive through stop.
Definition station_map.h:356
GetRoadStopType
RoadStopType GetRoadStopType(Tile t)
Get the road stop type of this tile.
Definition station_map.h:56
RoadStopType
RoadStopType
Types of RoadStops.
Definition station_type.h:43
stdafx.h
Definition of base types and functions in a cross-platform compatible way.
BaseStation::GetByTile
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
Definition base_station_base.h:160
GroundVehicleCache::cached_total_length
uint16_t cached_total_length
Length of the whole vehicle (valid only for the first engine).
Definition ground_vehicle.hpp:43
GroundVehicle::gcache
GroundVehicleCache gcache
Cache of often calculated values.
Definition ground_vehicle.hpp:83
Pool
Base class for all pools.
Definition pool_type.hpp:133
RoadStop::Entries
Container for both east and west entry points.
Definition roadstop_base.h:64
RoadStop::Entries::west
Entry west
Information for vehicles that entered from the west.
Definition roadstop_base.h:66
RoadStop::Entries::east
Entry east
Information for vehicles that entered from the east.
Definition roadstop_base.h:65
RoadStop::Entry
Container for each entry point of a drive through road stop.
Definition roadstop_base.h:31
RoadStop::Entry::occupied
uint16_t occupied
The amount of occupied stop in tile 'units'.
Definition roadstop_base.h:34
RoadStop::Entry::Leave
void Leave(const RoadVehicle *rv)
Leave the road stop.
Definition roadstop.cpp:267
RoadStop::Entry::Enter
void Enter(const RoadVehicle *rv)
Enter the road stop.
Definition roadstop.cpp:277
RoadStop::Entry::CheckIntegrity
void CheckIntegrity(const RoadStop *rs) const
Check the integrity of the data in this struct.
Definition roadstop.cpp:358
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:321
RoadStop::Entry::length
uint16_t length
The length of the stop in tile 'units'.
Definition roadstop_base.h:33
RoadStop
A Stop for a Road Vehicle.
Definition roadstop_base.h:21
RoadStop::SetEntranceBusy
void SetEntranceBusy(bool busy)
Makes an entrance occupied or free.
Definition roadstop_base.h:114
RoadStop::RoadStopStatusFlag::BaseEntry
@ BaseEntry
Non-zero when the entries on this road stop are the primary, i.e. the ones to delete.
RoadStop::Leave
void Leave(RoadVehicle *rv)
Leave the road stop.
Definition roadstop.cpp:203
RoadStop::next
RoadStop * next
Next stop of the given type at this station.
Definition roadstop_base.h:71
RoadStop::entries
Entries * entries
Information about available and allocated bays.
Definition roadstop_base.h:152
RoadStop::AllocateBay
uint AllocateBay()
Allocates a bay.
Definition roadstop_base.h:159
RoadStop::GetNextRoadStop
RoadStop * GetNextRoadStop(const struct RoadVehicle *v) const
Get the next road stop accessible by this vehicle.
Definition roadstop.cpp:40
RoadStop::HasFreeBay
bool HasFreeBay() const
Checks whether there is a free bay in this road stop.
Definition roadstop_base.h:82
RoadStop::Enter
bool Enter(RoadVehicle *rv)
Enter the road stop.
Definition roadstop.cpp:220
RoadStop::FreeBay
void FreeBay(uint nr)
Frees the given bay.
Definition roadstop_base.h:188
RoadStop::xy
TileIndex xy
Position on the map.
Definition roadstop_base.h:70
RoadStop::IsEntranceBusy
bool IsEntranceBusy() const
Checks whether the entrance of the road stop is occupied by a vehicle.
Definition roadstop_base.h:105
RoadStop::status
RoadStopStatusFlags status
Current status of the Stop. Access using *Bay and *Busy functions.
Definition roadstop_base.h:69
RoadStop::GetEntry
const Entry & GetEntry(DiagDirection dir) const
Get the drive through road stop entry struct for the given direction.
Definition roadstop_base.h:124
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:292
RoadStop::GetByTile
static RoadStop * GetByTile(TileIndex tile, RoadStopType type)
Find a roadstop at given tile.
Definition roadstop.cpp:253
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:60
RoadStop::ClearDriveThrough
void ClearDriveThrough()
Prepare for removal of this stop; update other neighbouring stops if needed.
Definition roadstop.cpp:122
RoadVehicle
Buses, trucks and trams belong to this class.
Definition roadveh.h:98
RoadVehicle::state
uint8_t state
Definition roadveh.h:100
RoadVehicle::compatible_roadtypes
RoadTypes compatible_roadtypes
NOSAVE: Roadtypes this consist is powered on.
Definition roadveh.h:110
SpecializedVehicle::From
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
Definition vehicle_base.h:1134
Station
Station data structure.
Definition station_base.h:500
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > >
Vehicle
Vehicle data structure.
Definition vehicle_base.h:205
Vehicle::direction
Direction direction
facing
Definition vehicle_base.h:268
Vehicle::HasArticulatedPart
bool HasArticulatedPart() const
Check if an engine has an articulated part.
Definition vehicle_base.h:931
Vehicle::tile
TileIndex tile
Current tile index.
Definition vehicle_base.h:225
IsTileType
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
TILE_SIZE
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
MP_STATION
@ MP_STATION
A tile of a station.
Definition tile_type.h:53
VehState::Crashed
@ Crashed
Vehicle is crashed.
vehicle_func.h
Functions related to vehicles.
VEH_ROAD
@ VEH_ROAD
Road vehicle type.
Definition vehicle_type.h:26