# roadstop.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "roadveh.h"
    00014 #include "core/pool_func.hpp"
    00015 #include "roadstop_base.h"
    00016 #include "station_base.h"
    00017 #include "vehicle_func.h"
    00018
    00019 RoadStopPool _roadstop_pool("RoadStop");
    00020 INSTANTIATE_POOL_METHODS(RoadStop)
    00021
    00022
    00025 RoadStop::~RoadStop()
    00026 {
    00027   /* When we are the head we need to free the entries */
    00028   if (HasBit(this->status, RSSFB_BASE_ENTRY)) {
    00029     delete this->east;
    00030     delete this->west;
    00031   }
    00032
    00033   if (CleaningPool()) return;
    00034 }
    00035
    00041 RoadStop *RoadStop::GetNextRoadStop(const RoadVehicle *v) const
    00042 {
    00043   for (RoadStop *rs = this->next; rs != NULL; rs = rs->next) {
    00044     /* The vehicle cannot go to this roadstop (different roadtype) */
    00045     if ((GetRoadTypes(rs->xy) & v->compatible_roadtypes) == ROADTYPES_NONE) continue;
    00046     /* The vehicle is articulated and can therefor not go the a standard road stop */
    00047     if (IsStandardRoadStopTile(rs->xy) && v->HasArticulatedPart()) continue;
    00048
    00049     /* The vehicle can actually go to this road stop. So, return it! */
    00050     return rs;
    00051   }
    00052
    00053   return NULL;
    00054 }
    00055
    00061 void RoadStop::MakeDriveThrough()
    00062 {
    00063   assert(this->east == NULL && this->west == NULL);
    00064
    00065   RoadStopType rst = GetRoadStopType(this->xy);
    00066   DiagDirection dir = GetRoadStopDir(this->xy);
    00067   /* Use absolute so we always go towards the nortern tile */
    00068   TileIndexDiff offset = abs(TileOffsByDiagDir(dir));
    00069
    00070   /* Information about the tile north of us */
    00071   TileIndex north_tile = this->xy - offset;
    00072   bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
    00073   RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : NULL;
    00074
    00075   /* Information about the tile south of us */
    00076   TileIndex south_tile = this->xy + offset;
    00077   bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
    00078   RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : NULL;
    00079
    00080   /* Amount of road stops that will be added to the 'northern' head */
    00081   int added = 1;
    00082   if (north && rs_north->east != NULL) { // (east != NULL) == (west != NULL)
    00083     /* There is a more nothern one, so this can join them */
    00084     this->east = rs_north->east;
    00085     this->west = rs_north->west;
    00086
    00087     if (south && rs_south->east != NULL) { // (east != NULL) == (west != NULL)
    00088       /* There more southern tiles too, they must 'join' us too */
    00089       ClrBit(rs_south->status, RSSFB_BASE_ENTRY);
    00090       this->east->occupied += rs_south->east->occupied;
    00091       this->west->occupied += rs_south->west->occupied;
    00092
    00093       /* Free the now unneeded entry structs */
    00094       delete rs_south->east;
    00095       delete rs_south->west;
    00096
    00097       /* Make all 'children' of the southern tile take the new master */
    00098       for (; IsDriveThroughRoadStopContinuation(this->xy, south_tile); south_tile += offset) {
    00099         rs_south = RoadStop::GetByTile(south_tile, rst);
    00100         if (rs_south->east == NULL) break;
    00101         rs_south->east = rs_north->east;
    00102         rs_south->west = rs_north->west;
    00103         added++;
    00104       }
    00105     }
    00106   } else if (south && rs_south->east != NULL) { // (east != NULL) == (west != NULL)
    00107     /* There is one to the south, but not to the north... so we become 'parent' */
    00108     this->east = rs_south->east;
    00109     this->west = rs_south->west;
    00110     SetBit(this->status, RSSFB_BASE_ENTRY);
    00111     ClrBit(rs_south->status, RSSFB_BASE_ENTRY);
    00112   } else {
    00113     /* We are the only... so we are automatically the master */
    00114     this->east = new Entry();
    00115     this->west = new Entry();
    00116     SetBit(this->status, RSSFB_BASE_ENTRY);
    00117   }
    00118
    00119   /* Now update the lengths */
    00120   added *= TILE_SIZE;
    00121   this->east->length += added;
    00122   this->west->length += added;
    00123 }
    00124
    00129 void RoadStop::ClearDriveThrough()
    00130 {
    00131   assert(this->east != NULL && this->west != NULL);
    00132
    00133   RoadStopType rst = GetRoadStopType(this->xy);
    00134   DiagDirection dir = GetRoadStopDir(this->xy);
    00135   /* Use absolute so we always go towards the nortern tile */
    00136   TileIndexDiff offset = abs(TileOffsByDiagDir(dir));
    00137
    00138   /* Information about the tile north of us */
    00139   TileIndex north_tile = this->xy - offset;
    00140   bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
    00141   RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : NULL;
    00142
    00143   /* Information about the tile south of us */
    00144   TileIndex south_tile = this->xy + offset;
    00145   bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
    00146   RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : NULL;
    00147
    00148   /* Must only be cleared after we determined which neighbours are
    00149    * part of our little entry 'queue' */
    00150   DoClearSquare(this->xy);
    00151
    00152   if (north) {
    00153     /* There is a tile to the north, so we can't clear ourselves. */
    00154     if (south) {
    00155       /* There are more southern tiles too, they must be split;
    00156        * first make the new southern 'base' */
    00157       SetBit(rs_south->status, RSSFB_BASE_ENTRY);
    00158       rs_south->east = new Entry();
    00159       rs_south->west = new Entry();
    00160
    00161       /* Keep track of the base because we need it later on */
    00162       RoadStop *rs_south_base = rs_south;
    00163       TileIndex base_tile = south_tile;
    00164
    00165       /* Make all (even more) southern stops part of the new entry queue */
    00166       for (south_tile += offset; IsDriveThroughRoadStopContinuation(base_tile, south_tile); south_tile += offset) {
    00167         rs_south = RoadStop::GetByTile(south_tile, rst);
    00168         rs_south->east = rs_south_base->east;
    00169         rs_south->west = rs_south_base->west;
    00170       }
    00171
    00172       /* Find the other end; the northern most tile */
    00173       for (; IsDriveThroughRoadStopContinuation(base_tile, north_tile); north_tile -= offset) {
    00174         rs_north = RoadStop::GetByTile(north_tile, rst);
    00175       }
    00176
    00177       /* We have to rebuild the entries because we cannot easily determine
    00178        * how full each part is. So instead of keeping and maintaining a list
    00179        * of vehicles and using that to 'rebuild' the occupied state we just
    00180        * rebuild it from scratch as that removes lots of maintainance code
    00181        * for the vehicle list and it's faster in real games as long as you
    00182        * do not keep split and merge road stop every tick by the millions. */
    00183       rs_south_base->east->Rebuild(rs_south_base);
    00184       rs_south_base->west->Rebuild(rs_south_base);
    00185
    00186       assert(HasBit(rs_north->status, RSSFB_BASE_ENTRY));
    00187       rs_north->east->Rebuild(rs_north);
    00188       rs_north->west->Rebuild(rs_north);
    00189     } else {
    00190       /* Only we left, so simple update the length. */
    00191       rs_north->east->length -= TILE_SIZE;
    00192       rs_north->west->length -= TILE_SIZE;
    00193     }
    00194   } else if (south) {
    00195     /* There is only something to the south. Hand over the base entry */
    00196     SetBit(rs_south->status, RSSFB_BASE_ENTRY);
    00197     rs_south->east->length -= TILE_SIZE;
    00198     rs_south->west->length -= TILE_SIZE;
    00199   } else {
    00200     /* We were the last */
    00201     delete this->east;
    00202     delete this->west;
    00203   }
    00204
    00205   /* Make sure we don't get used for something 'incorrect' */
    00206   ClrBit(this->status, RSSFB_BASE_ENTRY);
    00207   this->east = NULL;
    00208   this->west = NULL;
    00209 }
    00210
    00215 void RoadStop::Leave(RoadVehicle *rv)
    00216 {
    00217   if (IsStandardRoadStopTile(rv->tile)) {
    00218     /* Vehicle is leaving a road stop tile, mark bay as free */
    00219     this->FreeBay(HasBit(rv->state, RVS_USING_SECOND_BAY));
    00220     this->SetEntranceBusy(false);
    00221   } else {
    00222     /* Otherwise just leave the drive through's entry cache. */
    00223     this->GetEntry(DirToDiagDir(rv->direction))->Leave(rv);
    00224   }
    00225 }
    00226
    00232 bool RoadStop::Enter(RoadVehicle *rv)
    00233 {
    00234   if (IsStandardRoadStopTile(this->xy)) {
    00235     /* For normal (non drive-through) road stops
    00236      * Check if station is busy or if there are no free bays or whether it is a articulated vehicle. */
    00237     if (this->IsEntranceBusy() || !this->HasFreeBay() || rv->HasArticulatedPart()) return false;
    00238
    00239     SetBit(rv->state, RVS_IN_ROAD_STOP);
    00240
    00241     /* Allocate a bay and update the road state */
    00242     uint bay_nr = this->AllocateBay();
    00243     SB(rv->state, RVS_USING_SECOND_BAY, 1, bay_nr);
    00244
    00245     /* Mark the station entrace as busy */
    00246     this->SetEntranceBusy(true);
    00247     return true;
    00248   }
    00249
    00250   /* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */
    00251   this->GetEntry(DirToDiagDir(rv->direction))->Enter(rv);
    00252
    00253   /* Indicate a drive-through stop */
    00254   SetBit(rv->state, RVS_IN_DT_ROAD_STOP);
    00255   return true;
    00256 }
    00257
    00265 /* static */ RoadStop *RoadStop::GetByTile(TileIndex tile, RoadStopType type)
    00266 {
    00267   const Station *st = Station::GetByTile(tile);
    00268
    00269   for (RoadStop *rs = st->GetPrimaryRoadStop(type);; rs = rs->next) {
    00270     if (rs->xy == tile) return rs;
    00271     assert(rs->next != NULL);
    00272   }
    00273 }
    00274
    00279 void RoadStop::Entry::Leave(const RoadVehicle *rv)
    00280 {
    00281   this->occupied -= rv->gcache.cached_total_length;
    00282   assert(this->occupied >= 0);
    00283 }
    00284
    00289 void RoadStop::Entry::Enter(const RoadVehicle *rv)
    00290 {
    00291   /* we cannot assert on this->occupied < this->length because of the
    00292    * remote possibility that RVs are running through eachother when
    00293    * trying to prevention an infinite jam. */
    00294   this->occupied += rv->gcache.cached_total_length;
    00295 }
    00296
    00304 /* static */ bool RoadStop::IsDriveThroughRoadStopContinuation(TileIndex rs, TileIndex next)
    00305 {
    00306   return IsTileType(next, MP_STATION) &&
    00307       GetStationIndex(next) == GetStationIndex(rs) &&
    00308       GetStationType(next) == GetStationType(rs) &&
    00309       GetRoadStopDir(next) == GetRoadStopDir(rs) &&
    00310       IsDriveThroughStopTile(next);
    00311 }
    00312
    00313 typedef std::list<const RoadVehicle *> RVList;
    00314
    00316 struct RoadStopEntryRebuilderHelper {
    00317   RVList vehicles;
    00318   DiagDirection dir;
    00319 };
    00320
    00327 Vehicle *FindVehiclesInRoadStop(Vehicle *v, void *data)
    00328 {
    00329   RoadStopEntryRebuilderHelper *rserh = (RoadStopEntryRebuilderHelper*)data;
    00330   /* Not a RV or not in the right direction or crashed :( */
    00331   if (v->type != VEH_ROAD || DirToDiagDir(v->direction) != rserh->dir || !v->IsPrimaryVehicle() || (v->vehstatus & VS_CRASHED) != 0) return NULL;
    00332
    00333   RoadVehicle *rv = RoadVehicle::From(v);
    00334   /* Don't add ones not in a road stop */
    00335   if (rv->state < RVSB_IN_ROAD_STOP) return NULL;
    00336
    00337   /* Do not add duplicates! */
    00338   for (RVList::iterator it = rserh->vehicles.begin(); it != rserh->vehicles.end(); it++) {
    00339     if (rv == *it) return NULL;
    00340   }
    00341
    00342   rserh->vehicles.push_back(rv);
    00343   return NULL;
    00344 }
    00345
    00351 void RoadStop::Entry::Rebuild(const RoadStop *rs, int side)
    00352 {
    00353   assert(HasBit(rs->status, RSSFB_BASE_ENTRY));
    00354
    00355   DiagDirection dir = GetRoadStopDir(rs->xy);
    00356   if (side == -1) side = (rs->east == this);
    00357
    00358   RoadStopEntryRebuilderHelper rserh;
    00359   rserh.dir = side ? dir : ReverseDiagDir(dir);
    00360
    00361   this->length = 0;
    00362   TileIndexDiff offset = abs(TileOffsByDiagDir(dir));
    00363   for (TileIndex tile = rs->xy; IsDriveThroughRoadStopContinuation(rs->xy, tile); tile += offset) {
    00364     this->length += TILE_SIZE;
    00365     FindVehicleOnPos(tile, &rserh, FindVehiclesInRoadStop);
    00366   }
    00367
    00368   this->occupied = 0;
    00369   for (RVList::iterator it = rserh.vehicles.begin(); it != rserh.vehicles.end(); it++) {
    00370     this->occupied += (*it)->gcache.cached_total_length;
    00371   }
    00372 }
    00373
    00374
    00379 void RoadStop::Entry::CheckIntegrity(const RoadStop *rs) const
    00380 {
    00381   if (!HasBit(rs->status, RSSFB_BASE_ENTRY)) return;
    00382
    00383   /* The tile 'before' the road stop must not be part of this 'line' */
    00384   assert(!IsDriveThroughRoadStopContinuation(rs->xy, rs->xy - abs(TileOffsByDiagDir(GetRoadStopDir(rs->xy)))));
    00385
    00386   Entry temp;
    00387   temp.Rebuild(rs, rs->east == this);
    00388   if (temp.length != this->length || temp.occupied != this->occupied) NOT_REACHED();
    00389 }
    00390
    00391
    00392 void InitializeRoadStops()
    00393 {
    00394   _roadstop_pool.CleanPool();
    00395 }