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"
19 /** The pool of roadstops. */
20 template<> RoadStop::Pool
RoadStop::PoolItem::pool ("RoadStop");
21 INSTANTIATE_POOL_METHODS(RoadStop
)
24 * De-Initializes RoadStops.
28 /* When we are the head we need to free the entries */
29 if (HasBit(this->status
, RSSFB_BASE_ENTRY
)) {
30 delete this->platform
;
33 if (CleaningPool()) return;
37 * Join this road stop to another 'base' road stop if possible;
38 * fill all necessary data to become an actual drive through road stop.
39 * Also update the length etc.
41 void RoadStop::MakeDriveThrough()
43 assert (this->platform
== NULL
);
45 RoadStopType rst
= GetRoadStopType(this->xy
);
46 /* AxisToDiagDir always returns the direction that heads south. */
47 TileIndexDiff offset
= TileOffsByDiagDir(AxisToDiagDir(GetRoadStopAxis(this->xy
)));
49 /* Information about the tile north of us */
50 TileIndex north_tile
= this->xy
- offset
;
51 bool north
= IsDriveThroughRoadStopContinuation(this->xy
, north_tile
);
52 RoadStop
*rs_north
= north
? RoadStop::GetByTile(north_tile
, rst
) : NULL
;
54 /* Information about the tile south of us */
55 TileIndex south_tile
= this->xy
+ offset
;
56 bool south
= IsDriveThroughRoadStopContinuation(this->xy
, south_tile
);
57 RoadStop
*rs_south
= south
? RoadStop::GetByTile(south_tile
, rst
) : NULL
;
59 /* Amount of road stops that will be added to the 'northern' head */
61 if (north
&& rs_north
->platform
!= NULL
) {
62 /* There is a more northern one, so this can join them */
63 this->platform
= rs_north
->platform
;
65 if (south
&& rs_south
->platform
!= NULL
) {
66 /* There more southern tiles too, they must 'join' us too */
67 ClrBit(rs_south
->status
, RSSFB_BASE_ENTRY
);
68 this->platform
->occupied_east
+= rs_south
->platform
->occupied_east
;
69 this->platform
->occupied_west
+= rs_south
->platform
->occupied_west
;
71 /* Free the now unneeded entry structs */
72 delete rs_south
->platform
;
74 /* Make all 'children' of the southern tile take the new master */
75 for (; IsDriveThroughRoadStopContinuation(this->xy
, south_tile
); south_tile
+= offset
) {
76 rs_south
= RoadStop::GetByTile(south_tile
, rst
);
77 if (rs_south
->platform
== NULL
) break;
78 rs_south
->platform
= rs_north
->platform
;
82 } else if (south
&& rs_south
->platform
!= NULL
) {
83 /* There is one to the south, but not to the north... so we become 'parent' */
84 this->platform
= rs_south
->platform
;
85 SetBit(this->status
, RSSFB_BASE_ENTRY
);
86 ClrBit(rs_south
->status
, RSSFB_BASE_ENTRY
);
88 /* We are the only... so we are automatically the master */
89 this->platform
= new Platform();
90 SetBit(this->status
, RSSFB_BASE_ENTRY
);
93 /* Now update the lengths */
95 this->platform
->length
+= added
;
99 * Prepare for removal of this stop; update other neighbouring stops
100 * if needed. Also update the length etc.
102 void RoadStop::ClearDriveThrough()
104 assert (this->platform
!= NULL
);
106 RoadStopType rst
= GetRoadStopType(this->xy
);
107 /* AxisToDiagDir always returns the direction that heads south. */
108 TileIndexDiff offset
= TileOffsByDiagDir(AxisToDiagDir(GetRoadStopAxis(this->xy
)));
110 /* Information about the tile north of us */
111 TileIndex north_tile
= this->xy
- offset
;
112 bool north
= IsDriveThroughRoadStopContinuation(this->xy
, north_tile
);
113 RoadStop
*rs_north
= north
? RoadStop::GetByTile(north_tile
, rst
) : NULL
;
115 /* Information about the tile south of us */
116 TileIndex south_tile
= this->xy
+ offset
;
117 bool south
= IsDriveThroughRoadStopContinuation(this->xy
, south_tile
);
118 RoadStop
*rs_south
= south
? RoadStop::GetByTile(south_tile
, rst
) : NULL
;
120 /* Must only be cleared after we determined which neighbours are
121 * part of our little entry 'queue' */
122 DoClearSquare(this->xy
);
125 /* There is a tile to the north, so we can't clear ourselves. */
127 /* There are more southern tiles too, they must be split;
128 * first make the new southern 'base' */
129 SetBit(rs_south
->status
, RSSFB_BASE_ENTRY
);
130 rs_south
->platform
= new Platform();
132 /* Keep track of the base because we need it later on */
133 RoadStop
*rs_south_base
= rs_south
;
134 TileIndex base_tile
= south_tile
;
136 /* Make all (even more) southern stops part of the new entry queue */
137 for (south_tile
+= offset
; IsDriveThroughRoadStopContinuation(base_tile
, south_tile
); south_tile
+= offset
) {
138 rs_south
= RoadStop::GetByTile(south_tile
, rst
);
139 rs_south
->platform
= rs_south_base
->platform
;
142 /* Find the other end; the northern most tile */
143 for (; IsDriveThroughRoadStopContinuation(base_tile
, north_tile
); north_tile
-= offset
) {
144 rs_north
= RoadStop::GetByTile(north_tile
, rst
);
147 /* We have to rebuild the entries because we cannot easily determine
148 * how full each part is. So instead of keeping and maintaining a list
149 * of vehicles and using that to 'rebuild' the occupied state we just
150 * rebuild it from scratch as that removes lots of maintenance code
151 * for the vehicle list and it's faster in real games as long as you
152 * do not keep split and merge road stop every tick by the millions. */
153 rs_south_base
->Rebuild();
155 assert(HasBit(rs_north
->status
, RSSFB_BASE_ENTRY
));
158 /* Only we left, so simple update the length. */
159 rs_north
->platform
->length
-= TILE_SIZE
;
162 /* There is only something to the south. Hand over the base entry */
163 SetBit(rs_south
->status
, RSSFB_BASE_ENTRY
);
164 rs_south
->platform
->length
-= TILE_SIZE
;
166 /* We were the last */
167 delete this->platform
;
170 /* Make sure we don't get used for something 'incorrect' */
171 ClrBit(this->status
, RSSFB_BASE_ENTRY
);
172 this->platform
= NULL
;
176 * Find a roadstop at given tile
177 * @param tile tile with roadstop
178 * @param type roadstop type
179 * @return pointer to RoadStop
180 * @pre there has to be roadstop of given type there!
182 /* static */ RoadStop
*RoadStop::GetByTile(TileIndex tile
, RoadStopType type
)
184 const Station
*st
= Station::GetByTile(tile
);
186 for (RoadStop
*rs
= st
->GetPrimaryRoadStop(type
);; rs
= rs
->next
) {
187 if (rs
->xy
== tile
) return rs
;
188 assert(rs
->next
!= NULL
);
193 * Checks whether the 'next' tile is still part of the road same drive through
194 * stop 'rs' in the same direction for the same vehicle.
195 * @param rs the road stop tile to check against
196 * @param next the 'next' tile to check
197 * @return true if the 'next' tile is part of the road stop at 'next'.
199 /* static */ bool RoadStop::IsDriveThroughRoadStopContinuation(TileIndex rs
, TileIndex next
)
201 return IsStationTile(next
) &&
202 GetStationIndex(next
) == GetStationIndex(rs
) &&
203 GetStationType(next
) == GetStationType(rs
) &&
204 IsDriveThroughStopTile(next
) &&
205 GetRoadStopAxis(next
) == GetRoadStopAxis(rs
);
209 * Rebuild, from scratch, the vehicles and other metadata on this platform.
210 * @param tile The northernmost tile of the platform
212 void RoadStop::Platform::Rebuild (TileIndex tile
)
214 typedef std::list
<const RoadVehicle
*> RVList
; ///< A list of road vehicles
216 DiagDirection dir
= GetRoadStopDir (tile
);
217 TileIndexDiff offset
= abs(TileOffsByDiagDir(dir
));
218 Direction dir_east
= DiagDirToDir (dir
);
221 RVList list_east
, list_west
;
222 for (TileIndex t
= tile
; IsDriveThroughRoadStopContinuation (tile
, t
); t
+= offset
) {
224 VehicleTileIterator
iter (t
);
225 while (!iter
.finished()) {
226 Vehicle
*v
= iter
.next();
228 /* Not a RV or not primary or crashed :( */
229 if (v
->type
!= VEH_ROAD
|| !v
->IsPrimaryVehicle() || (v
->vehstatus
& VS_CRASHED
) != 0) continue;
231 RoadVehicle
*rv
= RoadVehicle::From(v
);
232 /* Don't add ones not in a road stop */
233 if (rv
->state
< RVSB_IN_ROAD_STOP
) continue;
235 assert (v
->direction
== dir_east
|| v
->direction
== ReverseDir (dir_east
));
236 RVList
*list
= (v
->direction
== dir_east
) ? &list_east
: &list_west
;
238 /* Do not add duplicates! */
240 for (it
= list
->begin(); it
!= list
->end(); it
++) {
241 if (rv
== *it
) break;
244 if (it
== list
->end()) list
->push_back(rv
);
248 this->length
= length
;
250 this->occupied_east
= 0;
251 for (RVList::iterator it
= list_east
.begin(); it
!= list_east
.end(); it
++) {
252 this->occupied_east
+= (*it
)->gcache
.cached_total_length
;
255 this->occupied_west
= 0;
256 for (RVList::iterator it
= list_west
.begin(); it
!= list_west
.end(); it
++) {
257 this->occupied_west
+= (*it
)->gcache
.cached_total_length
;
262 * Check the integrity of the data in this drive-through road stop.
264 void RoadStop::CheckIntegrity (void) const
266 if (!HasBit (this->status
, RSSFB_BASE_ENTRY
)) return;
268 /* The tile 'before' the road stop must not be part of this 'line' */
269 assert (!IsDriveThroughRoadStopContinuation (this->xy
, this->xy
- TileOffsByDiagDir (AxisToDiagDir (GetRoadStopAxis (this->xy
)))));
272 temp
.Rebuild (this->xy
);
274 assert (this->platform
->length
== temp
.length
);
275 assert (this->platform
->occupied_east
== temp
.occupied_east
);
276 assert (this->platform
->occupied_west
== temp
.occupied_west
);