1 /* $Id: roadstop.cpp 24900 2013-01-08 22:46:42Z planetmaker $ */
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file roadstop.cpp Implementation of the roadstop base class. */
14 #include "core/pool_func.hpp"
15 #include "roadstop_base.h"
16 #include "station_base.h"
17 #include "vehicle_func.h"
19 #include "safeguards.h"
21 /** The pool of roadstops. */
22 RoadStopPool
_roadstop_pool("RoadStop");
23 INSTANTIATE_POOL_METHODS(RoadStop
)
26 * De-Initializes RoadStops.
30 /* When we are the head we need to free the entries */
31 if (HasBit(this->status
, RSSFB_BASE_ENTRY
)) {
36 if (CleaningPool()) return;
40 * Get the next road stop accessible by this vehicle.
41 * @param v the vehicle to get the next road stop for.
42 * @return the next road stop accessible.
44 RoadStop
*RoadStop::GetNextRoadStop(const RoadVehicle
*v
) const
46 for (RoadStop
*rs
= this->next
; rs
!= NULL
; rs
= rs
->next
) {
47 /* The vehicle cannot go to this roadstop (different roadtype) */
48 if ((GetRoadTypes(rs
->xy
) & v
->compatible_roadtypes
) == ROADTYPES_NONE
) continue;
49 /* The vehicle is articulated and can therefore not go to a standard road stop. */
50 if (IsStandardRoadStopTile(rs
->xy
) && v
->HasArticulatedPart()) continue;
52 /* The vehicle can actually go to this road stop. So, return it! */
60 * Join this road stop to another 'base' road stop if possible;
61 * fill all necessary data to become an actual drive through road stop.
62 * Also update the length etc.
64 void RoadStop::MakeDriveThrough()
66 assert(this->east
== NULL
&& this->west
== NULL
);
68 RoadStopType rst
= GetRoadStopType(this->xy
);
69 DiagDirection dir
= GetRoadStopDir(this->xy
);
70 /* Use absolute so we always go towards the northern tile */
71 TileIndexDiff offset
= abs(TileOffsByDiagDir(dir
));
73 /* Information about the tile north of us */
74 TileIndex north_tile
= this->xy
- offset
;
75 bool north
= IsDriveThroughRoadStopContinuation(this->xy
, north_tile
);
76 RoadStop
*rs_north
= north
? RoadStop::GetByTile(north_tile
, rst
) : NULL
;
78 /* Information about the tile south of us */
79 TileIndex south_tile
= this->xy
+ offset
;
80 bool south
= IsDriveThroughRoadStopContinuation(this->xy
, south_tile
);
81 RoadStop
*rs_south
= south
? RoadStop::GetByTile(south_tile
, rst
) : NULL
;
83 /* Amount of road stops that will be added to the 'northern' head */
85 if (north
&& rs_north
->east
!= NULL
) { // (east != NULL) == (west != NULL)
86 /* There is a more northern one, so this can join them */
87 this->east
= rs_north
->east
;
88 this->west
= rs_north
->west
;
90 if (south
&& rs_south
->east
!= NULL
) { // (east != NULL) == (west != NULL)
91 /* There more southern tiles too, they must 'join' us too */
92 ClrBit(rs_south
->status
, RSSFB_BASE_ENTRY
);
93 this->east
->occupied
+= rs_south
->east
->occupied
;
94 this->west
->occupied
+= rs_south
->west
->occupied
;
96 /* Free the now unneeded entry structs */
97 delete rs_south
->east
;
98 delete rs_south
->west
;
100 /* Make all 'children' of the southern tile take the new master */
101 for (; IsDriveThroughRoadStopContinuation(this->xy
, south_tile
); south_tile
+= offset
) {
102 rs_south
= RoadStop::GetByTile(south_tile
, rst
);
103 if (rs_south
->east
== NULL
) break;
104 rs_south
->east
= rs_north
->east
;
105 rs_south
->west
= rs_north
->west
;
109 } else if (south
&& rs_south
->east
!= NULL
) { // (east != NULL) == (west != NULL)
110 /* There is one to the south, but not to the north... so we become 'parent' */
111 this->east
= rs_south
->east
;
112 this->west
= rs_south
->west
;
113 SetBit(this->status
, RSSFB_BASE_ENTRY
);
114 ClrBit(rs_south
->status
, RSSFB_BASE_ENTRY
);
116 /* We are the only... so we are automatically the master */
117 this->east
= new Entry();
118 this->west
= new Entry();
119 SetBit(this->status
, RSSFB_BASE_ENTRY
);
122 /* Now update the lengths */
124 this->east
->length
+= added
;
125 this->west
->length
+= added
;
129 * Prepare for removal of this stop; update other neighbouring stops
130 * if needed. Also update the length etc.
132 void RoadStop::ClearDriveThrough()
134 assert(this->east
!= NULL
&& this->west
!= NULL
);
136 RoadStopType rst
= GetRoadStopType(this->xy
);
137 DiagDirection dir
= GetRoadStopDir(this->xy
);
138 /* Use absolute so we always go towards the northern tile */
139 TileIndexDiff offset
= abs(TileOffsByDiagDir(dir
));
141 /* Information about the tile north of us */
142 TileIndex north_tile
= this->xy
- offset
;
143 bool north
= IsDriveThroughRoadStopContinuation(this->xy
, north_tile
);
144 RoadStop
*rs_north
= north
? RoadStop::GetByTile(north_tile
, rst
) : NULL
;
146 /* Information about the tile south of us */
147 TileIndex south_tile
= this->xy
+ offset
;
148 bool south
= IsDriveThroughRoadStopContinuation(this->xy
, south_tile
);
149 RoadStop
*rs_south
= south
? RoadStop::GetByTile(south_tile
, rst
) : NULL
;
151 /* Must only be cleared after we determined which neighbours are
152 * part of our little entry 'queue' */
153 DoClearSquare(this->xy
);
156 /* There is a tile to the north, so we can't clear ourselves. */
158 /* There are more southern tiles too, they must be split;
159 * first make the new southern 'base' */
160 SetBit(rs_south
->status
, RSSFB_BASE_ENTRY
);
161 rs_south
->east
= new Entry();
162 rs_south
->west
= new Entry();
164 /* Keep track of the base because we need it later on */
165 RoadStop
*rs_south_base
= rs_south
;
166 TileIndex base_tile
= south_tile
;
168 /* Make all (even more) southern stops part of the new entry queue */
169 for (south_tile
+= offset
; IsDriveThroughRoadStopContinuation(base_tile
, south_tile
); south_tile
+= offset
) {
170 rs_south
= RoadStop::GetByTile(south_tile
, rst
);
171 rs_south
->east
= rs_south_base
->east
;
172 rs_south
->west
= rs_south_base
->west
;
175 /* Find the other end; the northern most tile */
176 for (; IsDriveThroughRoadStopContinuation(base_tile
, north_tile
); north_tile
-= offset
) {
177 rs_north
= RoadStop::GetByTile(north_tile
, rst
);
180 /* We have to rebuild the entries because we cannot easily determine
181 * how full each part is. So instead of keeping and maintaining a list
182 * of vehicles and using that to 'rebuild' the occupied state we just
183 * rebuild it from scratch as that removes lots of maintenance code
184 * for the vehicle list and it's faster in real games as long as you
185 * do not keep split and merge road stop every tick by the millions. */
186 rs_south_base
->east
->Rebuild(rs_south_base
);
187 rs_south_base
->west
->Rebuild(rs_south_base
);
189 assert(HasBit(rs_north
->status
, RSSFB_BASE_ENTRY
));
190 rs_north
->east
->Rebuild(rs_north
);
191 rs_north
->west
->Rebuild(rs_north
);
193 /* Only we left, so simple update the length. */
194 rs_north
->east
->length
-= TILE_SIZE
;
195 rs_north
->west
->length
-= TILE_SIZE
;
198 /* There is only something to the south. Hand over the base entry */
199 SetBit(rs_south
->status
, RSSFB_BASE_ENTRY
);
200 rs_south
->east
->length
-= TILE_SIZE
;
201 rs_south
->west
->length
-= TILE_SIZE
;
203 /* We were the last */
208 /* Make sure we don't get used for something 'incorrect' */
209 ClrBit(this->status
, RSSFB_BASE_ENTRY
);
215 * Leave the road stop
216 * @param rv the vehicle that leaves the stop
218 void RoadStop::Leave(RoadVehicle
*rv
)
220 if (IsStandardRoadStopTile(rv
->tile
)) {
221 /* Vehicle is leaving a road stop tile, mark bay as free */
222 this->FreeBay(HasBit(rv
->state
, RVS_USING_SECOND_BAY
));
223 this->SetEntranceBusy(false);
225 /* Otherwise just leave the drive through's entry cache. */
226 this->GetEntry(DirToDiagDir(rv
->direction
))->Leave(rv
);
231 * Enter the road stop
232 * @param rv the vehicle that enters the stop
233 * @return whether the road stop could actually be entered
235 bool RoadStop::Enter(RoadVehicle
*rv
)
237 if (IsStandardRoadStopTile(this->xy
)) {
238 /* For normal (non drive-through) road stops
239 * Check if station is busy or if there are no free bays or whether it is a articulated vehicle. */
240 if (this->IsEntranceBusy() || !this->HasFreeBay() || rv
->HasArticulatedPart()) return false;
242 SetBit(rv
->state
, RVS_IN_ROAD_STOP
);
244 /* Allocate a bay and update the road state */
245 uint bay_nr
= this->AllocateBay();
246 SB(rv
->state
, RVS_USING_SECOND_BAY
, 1, bay_nr
);
248 /* Mark the station entrance as busy */
249 this->SetEntranceBusy(true);
253 /* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */
254 this->GetEntry(DirToDiagDir(rv
->direction
))->Enter(rv
);
256 /* Indicate a drive-through stop */
257 SetBit(rv
->state
, RVS_IN_DT_ROAD_STOP
);
262 * Find a roadstop at given tile
263 * @param tile tile with roadstop
264 * @param type roadstop type
265 * @return pointer to RoadStop
266 * @pre there has to be roadstop of given type there!
268 /* static */ RoadStop
*RoadStop::GetByTile(TileIndex tile
, RoadStopType type
)
270 const Station
*st
= Station::GetByTile(tile
);
272 for (RoadStop
*rs
= st
->GetPrimaryRoadStop(type
);; rs
= rs
->next
) {
273 if (rs
->xy
== tile
) return rs
;
274 assert(rs
->next
!= NULL
);
279 * Leave the road stop
280 * @param rv the vehicle that leaves the stop
282 void RoadStop::Entry::Leave(const RoadVehicle
*rv
)
284 this->occupied
-= rv
->gcache
.cached_total_length
;
285 assert(this->occupied
>= 0);
289 * Enter the road stop
290 * @param rv the vehicle that enters the stop
292 void RoadStop::Entry::Enter(const RoadVehicle
*rv
)
294 /* we cannot assert on this->occupied < this->length because of the
295 * remote possibility that RVs are running through each other when
296 * trying to prevention an infinite jam. */
297 this->occupied
+= rv
->gcache
.cached_total_length
;
301 * Checks whether the 'next' tile is still part of the road same drive through
302 * stop 'rs' in the same direction for the same vehicle.
303 * @param rs the road stop tile to check against
304 * @param next the 'next' tile to check
305 * @return true if the 'next' tile is part of the road stop at 'next'.
307 /* static */ bool RoadStop::IsDriveThroughRoadStopContinuation(TileIndex rs
, TileIndex next
)
309 return IsTileType(next
, MP_STATION
) &&
310 GetStationIndex(next
) == GetStationIndex(rs
) &&
311 GetStationType(next
) == GetStationType(rs
) &&
312 GetRoadStopDir(next
) == GetRoadStopDir(rs
) &&
313 IsDriveThroughStopTile(next
);
316 typedef std::list
<const RoadVehicle
*> RVList
; ///< A list of road vehicles
318 /** Helper for finding RVs in a road stop. */
319 struct RoadStopEntryRebuilderHelper
{
320 RVList vehicles
; ///< The list of vehicles to possibly add to.
321 DiagDirection dir
; ///< The direction the vehicle has to face to be added.
325 * Add road vehicles to the station's list if needed.
326 * @param v the found vehicle
327 * @param data the extra data used to make our decision
328 * @return always NULL
330 Vehicle
*FindVehiclesInRoadStop(Vehicle
*v
, void *data
)
332 RoadStopEntryRebuilderHelper
*rserh
= (RoadStopEntryRebuilderHelper
*)data
;
333 /* Not a RV or not in the right direction or crashed :( */
334 if (v
->type
!= VEH_ROAD
|| DirToDiagDir(v
->direction
) != rserh
->dir
|| !v
->IsPrimaryVehicle() || (v
->vehstatus
& VS_CRASHED
) != 0) return NULL
;
336 RoadVehicle
*rv
= RoadVehicle::From(v
);
337 /* Don't add ones not in a road stop */
338 if (rv
->state
< RVSB_IN_ROAD_STOP
) return NULL
;
340 /* Do not add duplicates! */
341 for (RVList::iterator it
= rserh
->vehicles
.begin(); it
!= rserh
->vehicles
.end(); it
++) {
342 if (rv
== *it
) return NULL
;
345 rserh
->vehicles
.push_back(rv
);
350 * Rebuild, from scratch, the vehicles and other metadata on this stop.
351 * @param rs the roadstop this entry is part of
352 * @param side the side of the road stop to look at
354 void RoadStop::Entry::Rebuild(const RoadStop
*rs
, int side
)
356 assert(HasBit(rs
->status
, RSSFB_BASE_ENTRY
));
358 DiagDirection dir
= GetRoadStopDir(rs
->xy
);
359 if (side
== -1) side
= (rs
->east
== this);
361 RoadStopEntryRebuilderHelper rserh
;
362 rserh
.dir
= side
? dir
: ReverseDiagDir(dir
);
365 TileIndexDiff offset
= abs(TileOffsByDiagDir(dir
));
366 for (TileIndex tile
= rs
->xy
; IsDriveThroughRoadStopContinuation(rs
->xy
, tile
); tile
+= offset
) {
367 this->length
+= TILE_SIZE
;
368 FindVehicleOnPos(tile
, &rserh
, FindVehiclesInRoadStop
);
372 for (RVList::iterator it
= rserh
.vehicles
.begin(); it
!= rserh
.vehicles
.end(); it
++) {
373 this->occupied
+= (*it
)->gcache
.cached_total_length
;
379 * Check the integrity of the data in this struct.
380 * @param rs the roadstop this entry is part of
382 void RoadStop::Entry::CheckIntegrity(const RoadStop
*rs
) const
384 if (!HasBit(rs
->status
, RSSFB_BASE_ENTRY
)) return;
386 /* The tile 'before' the road stop must not be part of this 'line' */
387 assert(!IsDriveThroughRoadStopContinuation(rs
->xy
, rs
->xy
- abs(TileOffsByDiagDir(GetRoadStopDir(rs
->xy
)))));
390 temp
.Rebuild(rs
, rs
->east
== this);
391 if (temp
.length
!= this->length
|| temp
.occupied
!= this->occupied
) NOT_REACHED();