OpenTTD: src/roadstop.cpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

roadstop.cpp
Go to the documentation of this file.
1 /* $Id: roadstop.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
2
3 /*
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/>.
8 */
9
12 #include "stdafx.h"
13 #include "roadveh.h"
14 #include "core/pool_func.hpp"
15 #include "roadstop_base.h"
16 #include "station_base.h"
17 #include "vehicle_func.h"
18
19 #include "safeguards.h"
20
22 RoadStopPool _roadstop_pool("RoadStop");
23 INSTANTIATE_POOL_METHODS(RoadStop)
24
25
28 RoadStop::~RoadStop()
29 {
30 /* When we are the head we need to free the entries */
31 if (HasBit(this->status, RSSFB_BASE_ENTRY)) {
32 delete this->east;
33 delete this->west;
34 }
35
36 if (CleaningPool()) return;
37 }
38
44 RoadStop *RoadStop::GetNextRoadStop(const RoadVehicle *v) const
45 {
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;
51
52 /* The vehicle can actually go to this road stop. So, return it! */
53 return rs;
54 }
55
56 return NULL;
57 }
58
64 void RoadStop::MakeDriveThrough()
65 {
66 assert(this->east == NULL && this->west == NULL);
67
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));
72
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;
77
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;
82
83 /* Amount of road stops that will be added to the 'northern' head */
84 int added = 1;
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;
89
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;
95
96 /* Free the now unneeded entry structs */
97 delete rs_south->east;
98 delete rs_south->west;
99
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;
106 added++;
107 }
108 }
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);
115 } else {
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);
120 }
121
122 /* Now update the lengths */
123 added *= TILE_SIZE;
124 this->east->length += added;
125 this->west->length += added;
126 }
127
132 void RoadStop::ClearDriveThrough()
133 {
134 assert(this->east != NULL && this->west != NULL);
135
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));
140
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;
145
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;
150
151 /* Must only be cleared after we determined which neighbours are
152 * part of our little entry 'queue' */
153 DoClearSquare(this->xy);
154
155 if (north) {
156 /* There is a tile to the north, so we can't clear ourselves. */
157 if (south) {
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();
163
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;
167
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;
173 }
174
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);
178 }
179
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);
188
189 assert(HasBit(rs_north->status, RSSFB_BASE_ENTRY));
190 rs_north->east->Rebuild(rs_north);
191 rs_north->west->Rebuild(rs_north);
192 } else {
193 /* Only we left, so simple update the length. */
194 rs_north->east->length -= TILE_SIZE;
195 rs_north->west->length -= TILE_SIZE;
196 }
197 } else if (south) {
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;
202 } else {
203 /* We were the last */
204 delete this->east;
205 delete this->west;
206 }
207
208 /* Make sure we don't get used for something 'incorrect' */
209 ClrBit(this->status, RSSFB_BASE_ENTRY);
210 this->east = NULL;
211 this->west = NULL;
212 }
213
218 void RoadStop::Leave(RoadVehicle *rv)
219 {
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);
224 } else {
225 /* Otherwise just leave the drive through's entry cache. */
226 this->GetEntry(DirToDiagDir(rv->direction))->Leave(rv);
227 }
228 }
229
235 bool RoadStop::Enter(RoadVehicle *rv)
236 {
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;
241
242 SetBit(rv->state, RVS_IN_ROAD_STOP);
243
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);
247
248 /* Mark the station entrance as busy */
249 this->SetEntranceBusy(true);
250 return true;
251 }
252
253 /* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */
254 this->GetEntry(DirToDiagDir(rv->direction))->Enter(rv);
255
256 /* Indicate a drive-through stop */
257 SetBit(rv->state, RVS_IN_DT_ROAD_STOP);
258 return true;
259 }
260
268 /* static */ RoadStop *RoadStop::GetByTile(TileIndex tile, RoadStopType type)
269 {
270 const Station *st = Station::GetByTile(tile);
271
272 for (RoadStop *rs = st->GetPrimaryRoadStop(type);; rs = rs->next) {
273 if (rs->xy == tile) return rs;
274 assert(rs->next != NULL);
275 }
276 }
277
282 void RoadStop::Entry::Leave(const RoadVehicle *rv)
283 {
284 this->occupied -= rv->gcache.cached_total_length;
285 assert(this->occupied >= 0);
286 }
287
292 void RoadStop::Entry::Enter(const RoadVehicle *rv)
293 {
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;
298 }
299
307 /* static */ bool RoadStop::IsDriveThroughRoadStopContinuation(TileIndex rs, TileIndex next)
308 {
309 return IsTileType(next, MP_STATION) &&
310 GetStationIndex(next) == GetStationIndex(rs) &&
311 GetStationType(next) == GetStationType(rs) &&
312 GetRoadStopDir(next) == GetRoadStopDir(rs) &&
313 IsDriveThroughStopTile(next);
314 }
315
316 typedef std::list<const RoadVehicle *> RVList;
317
319 struct RoadStopEntryRebuilderHelper {
320 RVList vehicles;
321 DiagDirection dir;
322 };
323
330 Vehicle *FindVehiclesInRoadStop(Vehicle *v, void *data)
331 {
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;
335
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;
339
340 /* Do not add duplicates! */
341 for (RVList::iterator it = rserh->vehicles.begin(); it != rserh->vehicles.end(); it++) {
342 if (rv == *it) return NULL;
343 }
344
345 rserh->vehicles.push_back(rv);
346 return NULL;
347 }
348
354 void RoadStop::Entry::Rebuild(const RoadStop *rs, int side)
355 {
356 assert(HasBit(rs->status, RSSFB_BASE_ENTRY));
357
358 DiagDirection dir = GetRoadStopDir(rs->xy);
359 if (side == -1) side = (rs->east == this);
360
361 RoadStopEntryRebuilderHelper rserh;
362 rserh.dir = side ? dir : ReverseDiagDir(dir);
363
364 this->length = 0;
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);
369 }
370
371 this->occupied = 0;
372 for (RVList::iterator it = rserh.vehicles.begin(); it != rserh.vehicles.end(); it++) {
373 this->occupied += (*it)->gcache.cached_total_length;
374 }
375 }
376
377
382 void RoadStop::Entry::CheckIntegrity(const RoadStop *rs) const
383 {
384 if (!HasBit(rs->status, RSSFB_BASE_ENTRY)) return;
385
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)))));
388
389 Entry temp;
390 temp.Rebuild(rs, rs->east == this);
391 if (temp.length != this->length || temp.occupied != this->occupied) NOT_REACHED();
392 }