station.cpp

Go to the documentation of this file.
00001 /* $Id: station.cpp 14347 2008-09-16 20:29:09Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "bridge_map.h"
00008 #include "debug.h"
00009 #include "station_map.h"
00010 #include "station.h"
00011 #include "town.h"
00012 #include "news.h"
00013 #include "saveload.h"
00014 #include "player_func.h"
00015 #include "airport.h"
00016 #include "sprite.h"
00017 #include "depot.h"
00018 #include "train.h"
00019 #include "water_map.h"
00020 #include "industry_map.h"
00021 #include "newgrf_callbacks.h"
00022 #include "newgrf_station.h"
00023 #include "yapf/yapf.h"
00024 #include "cargotype.h"
00025 #include "roadveh.h"
00026 #include "station_gui.h"
00027 #include "zoom_func.h"
00028 #include "functions.h"
00029 #include "window_func.h"
00030 #include "date_func.h"
00031 #include "variables.h"
00032 #include "settings_type.h"
00033 #include "command_func.h"
00034 #include "aircraft.h"
00035 
00036 #include "table/sprites.h"
00037 #include "table/strings.h"
00038 
00039 Station::Station(TileIndex tile)
00040 {
00041   DEBUG(station, cDebugCtorLevel, "I+%3d", index);
00042 
00043   xy = tile;
00044   airport_tile = dock_tile = train_tile = 0;
00045   bus_stops = truck_stops = NULL;
00046   had_vehicle_of_type = 0;
00047   time_since_load = 255;
00048   time_since_unload = 255;
00049   delete_ctr = 0;
00050   facilities = 0;
00051 
00052   last_vehicle_type = VEH_INVALID;
00053 
00054   random_bits = 0; // Random() must be called when station is really built (DC_EXEC)
00055   waiting_triggers = 0;
00056 }
00057 
00064 Station::~Station()
00065 {
00066   DEBUG(station, cDebugCtorLevel, "I-%3d", index);
00067 
00068   free(this->name);
00069   free(this->speclist);
00070 
00071   if (CleaningPool()) return;
00072 
00073   while (!loading_vehicles.empty()) {
00074     loading_vehicles.front()->LeaveStation();
00075   }
00076 
00077   Vehicle *v;
00078   FOR_ALL_VEHICLES(v) {
00079     if (v->type == VEH_AIRCRAFT && IsNormalAircraft(v) && v->u.air.targetairport == this->index) {
00080       v->u.air.targetairport = INVALID_STATION;
00081     }
00082   }
00083 
00084   MarkDirty();
00085   RebuildStationLists();
00086   InvalidateWindowClasses(WC_STATION_LIST);
00087 
00088   DeleteWindowById(WC_STATION_VIEW, index);
00089 
00090   /* Now delete all orders that go to the station */
00091   RemoveOrderFromAllVehicles(OT_GOTO_STATION, index);
00092 
00093   /* Subsidies need removal as well */
00094   DeleteSubsidyWithStation(index);
00095 
00096   xy = 0;
00097 
00098   for (CargoID c = 0; c < NUM_CARGO; c++) {
00099     goods[c].cargo.Truncate(0);
00100   }
00101 }
00102 
00103 
00109 RoadStop *Station::GetPrimaryRoadStop(const Vehicle *v) const
00110 {
00111   RoadStop *rs = this->GetPrimaryRoadStop(IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? RoadStop::BUS : RoadStop::TRUCK);
00112 
00113   for (; rs != NULL; rs = rs->next) {
00114     /* The vehicle cannot go to this roadstop (different roadtype) */
00115     if ((GetRoadTypes(rs->xy) & v->u.road.compatible_roadtypes) == ROADTYPES_NONE) continue;
00116     /* The vehicle is articulated and can therefor not go the a standard road stop */
00117     if (IsStandardRoadStopTile(rs->xy) && RoadVehHasArticPart(v)) continue;
00118 
00119     /* The vehicle can actually go to this road stop. So, return it! */
00120     break;
00121   }
00122 
00123   return rs;
00124 }
00125 
00128 void Station::AddFacility(byte new_facility_bit, TileIndex facil_xy)
00129 {
00130   if (facilities == 0) {
00131     xy = facil_xy;
00132     random_bits = Random();
00133   }
00134   facilities |= new_facility_bit;
00135   owner = _current_player;
00136   build_date = _date;
00137 }
00138 
00139 void Station::MarkDirty() const
00140 {
00141   if (sign.width_1 != 0) {
00142     InvalidateWindowWidget(WC_STATION_VIEW, index, SVW_CAPTION);
00143 
00144     /* We use ZOOM_LVL_MAX here, as every viewport can have an other zoom,
00145      *  and there is no way for us to know which is the biggest. So make the
00146      *  biggest area dirty, and we are safe for sure. */
00147     MarkAllViewportsDirty(
00148       sign.left - 6,
00149       sign.top,
00150       sign.left + ScaleByZoom(sign.width_1 + 12, ZOOM_LVL_MAX),
00151       sign.top + ScaleByZoom(12, ZOOM_LVL_MAX));
00152   }
00153 }
00154 
00155 void Station::MarkTilesDirty(bool cargo_change) const
00156 {
00157   TileIndex tile = train_tile;
00158   int w, h;
00159 
00160   /* XXX No station is recorded as 0, not INVALID_TILE... */
00161   if (tile == 0) return;
00162 
00163   /* cargo_change is set if we're refreshing the tiles due to cargo moving
00164    * around. */
00165   if (cargo_change) {
00166     /* Don't waste time updating if there are no custom station graphics
00167      * that might change. Even if there are custom graphics, they might
00168      * not change. Unfortunately we have no way of telling. */
00169     if (this->num_specs == 0) return;
00170   }
00171 
00172   for (h = 0; h < trainst_h; h++) {
00173     for (w = 0; w < trainst_w; w++) {
00174       if (TileBelongsToRailStation(tile)) {
00175         MarkTileDirtyByTile(tile);
00176       }
00177       tile += TileDiffXY(1, 0);
00178     }
00179     tile += TileDiffXY(-w, 1);
00180   }
00181 }
00182 
00183 bool Station::TileBelongsToRailStation(TileIndex tile) const
00184 {
00185   return IsTileType(tile, MP_STATION) && GetStationIndex(tile) == index && IsRailwayStation(tile);
00186 }
00187 
00193 uint Station::GetPlatformLength(TileIndex tile) const
00194 {
00195   TileIndex t;
00196   TileIndexDiff delta;
00197   uint len = 0;
00198   assert(TileBelongsToRailStation(tile));
00199 
00200   delta = (GetRailStationAxis(tile) == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
00201 
00202   t = tile;
00203   do {
00204     t -= delta;
00205     len++;
00206   } while (IsCompatibleTrainStationTile(t, tile));
00207 
00208   t = tile;
00209   do {
00210     t += delta;
00211     len++;
00212   } while (IsCompatibleTrainStationTile(t, tile));
00213 
00214   return len - 1;
00215 }
00216 
00223 uint Station::GetPlatformLength(TileIndex tile, DiagDirection dir) const
00224 {
00225   TileIndex start_tile = tile;
00226   uint length = 0;
00227   assert(IsRailwayStationTile(tile));
00228   assert(dir < DIAGDIR_END);
00229 
00230   do {
00231     length ++;
00232     tile += TileOffsByDiagDir(dir);
00233   } while (IsCompatibleTrainStationTile(tile, start_tile));
00234 
00235   return length;
00236 }
00237 
00241 bool Station::IsBuoy() const
00242 {
00243   return (had_vehicle_of_type & HVOT_BUOY) != 0;
00244 }
00245 
00246 
00247 /************************************************************************/
00248 /*                     StationRect implementation                       */
00249 /************************************************************************/
00250 
00251 StationRect::StationRect()
00252 {
00253   MakeEmpty();
00254 }
00255 
00256 void StationRect::MakeEmpty()
00257 {
00258   left = top = right = bottom = 0;
00259 }
00260 
00270 bool StationRect::PtInExtendedRect(int x, int y, int distance) const
00271 {
00272   return (left - distance <= x && x <= right + distance && top - distance <= y && y <= bottom + distance);
00273 }
00274 
00275 bool StationRect::IsEmpty() const
00276 {
00277   return (left == 0 || left > right || top > bottom);
00278 }
00279 
00280 bool StationRect::BeforeAddTile(TileIndex tile, StationRectMode mode)
00281 {
00282   int x = TileX(tile);
00283   int y = TileY(tile);
00284   if (IsEmpty()) {
00285     /* we are adding the first station tile */
00286     left = right = x;
00287     top = bottom = y;
00288 
00289   } else if (!PtInExtendedRect(x, y)) {
00290     /* current rect is not empty and new point is outside this rect */
00291     /* make new spread-out rectangle */
00292     Rect new_rect = {min(x, left), min(y, top), max(x, right), max(y, bottom)};
00293 
00294     /* check new rect dimensions against preset max */
00295     int w = new_rect.right - new_rect.left + 1;
00296     int h = new_rect.bottom - new_rect.top + 1;
00297     if (mode != ADD_FORCE && (w > _patches.station_spread || h > _patches.station_spread)) {
00298       assert(mode != ADD_TRY);
00299       _error_message = STR_306C_STATION_TOO_SPREAD_OUT;
00300       return false;
00301     }
00302 
00303     /* spread-out ok, return true */
00304     if (mode != ADD_TEST) {
00305       /* we should update the station rect */
00306       *this = new_rect;
00307     }
00308   } else {
00309     ; // new point is inside the rect, we don't need to do anything
00310   }
00311   return true;
00312 }
00313 
00314 bool StationRect::BeforeAddRect(TileIndex tile, int w, int h, StationRectMode mode)
00315 {
00316   return BeforeAddTile(tile, mode) && BeforeAddTile(TILE_ADDXY(tile, w - 1, h - 1), mode);
00317 }
00318 
00319 /*static*/ bool StationRect::ScanForStationTiles(StationID st_id, int left_a, int top_a, int right_a, int bottom_a)
00320 {
00321   TileIndex top_left = TileXY(left_a, top_a);
00322   int width = right_a - left_a + 1;
00323   int height = bottom_a - top_a + 1;
00324 
00325   BEGIN_TILE_LOOP(tile, width, height, top_left)
00326     if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st_id) return true;
00327   END_TILE_LOOP(tile, width, height, top_left);
00328 
00329   return false;
00330 }
00331 
00332 bool StationRect::AfterRemoveTile(Station *st, TileIndex tile)
00333 {
00334   int x = TileX(tile);
00335   int y = TileY(tile);
00336 
00337   /* look if removed tile was on the bounding rect edge
00338    * and try to reduce the rect by this edge
00339    * do it until we have empty rect or nothing to do */
00340   for (;;) {
00341     /* check if removed tile is on rect edge */
00342     bool left_edge = (x == left);
00343     bool right_edge = (x == right);
00344     bool top_edge = (y == top);
00345     bool bottom_edge = (y == bottom);
00346 
00347     /* can we reduce the rect in either direction? */
00348     bool reduce_x = ((left_edge || right_edge) && !ScanForStationTiles(st->index, x, top, x, bottom));
00349     bool reduce_y = ((top_edge || bottom_edge) && !ScanForStationTiles(st->index, left, y, right, y));
00350     if (!(reduce_x || reduce_y)) break; // nothing to do (can't reduce)
00351 
00352     if (reduce_x) {
00353       /* reduce horizontally */
00354       if (left_edge) {
00355         /* move left edge right */
00356         left = x = x + 1;
00357       } else {
00358         /* move right edge left */
00359         right = x = x - 1;
00360       }
00361     }
00362     if (reduce_y) {
00363       /* reduce vertically */
00364       if (top_edge) {
00365         /* move top edge down */
00366         top = y = y + 1;
00367       } else {
00368         /* move bottom edge up */
00369         bottom = y = y - 1;
00370       }
00371     }
00372 
00373     if (left > right || top > bottom) {
00374       /* can't continue, if the remaining rectangle is empty */
00375       MakeEmpty();
00376       return true; // empty remaining rect
00377     }
00378   }
00379   return false; // non-empty remaining rect
00380 }
00381 
00382 bool StationRect::AfterRemoveRect(Station *st, TileIndex tile, int w, int h)
00383 {
00384   assert(PtInExtendedRect(TileX(tile), TileY(tile)));
00385   assert(PtInExtendedRect(TileX(tile) + w - 1, TileY(tile) + h - 1));
00386 
00387   bool empty = AfterRemoveTile(st, tile);
00388   if (w != 1 || h != 1) empty = empty || AfterRemoveTile(st, TILE_ADDXY(tile, w - 1, h - 1));
00389   return empty;
00390 }
00391 
00392 StationRect& StationRect::operator = (Rect src)
00393 {
00394   left = src.left;
00395   top = src.top;
00396   right = src.right;
00397   bottom = src.bottom;
00398   return *this;
00399 }
00400 
00401 
00402 /************************************************************************/
00403 /*                     RoadStop implementation                          */
00404 /************************************************************************/
00405 
00407 RoadStop::RoadStop(TileIndex tile) :
00408   xy(tile),
00409   status(3), // stop is free
00410   num_vehicles(0),
00411   next(NULL)
00412 {
00413   DEBUG(ms, cDebugCtorLevel,  "I+ at %d[0x%x]", tile, tile);
00414 }
00415 
00419 RoadStop::~RoadStop()
00420 {
00421   if (CleaningPool()) return;
00422 
00423   /* Clear the slot assignment of all vehicles heading for this road stop */
00424   if (num_vehicles != 0) {
00425     Vehicle *v;
00426 
00427     FOR_ALL_VEHICLES(v) {
00428       if (v->type == VEH_ROAD && v->u.road.slot == this) ClearSlot(v);
00429     }
00430   }
00431   assert(num_vehicles == 0);
00432 
00433   DEBUG(ms, cDebugCtorLevel , "I- at %d[0x%x]", xy, xy);
00434 
00435   xy = 0;
00436 }
00437 
00439 bool RoadStop::HasFreeBay() const
00440 {
00441   return GB(status, 0, MAX_BAY_COUNT) != 0;
00442 }
00443 
00445 bool RoadStop::IsFreeBay(uint nr) const
00446 {
00447   assert(nr < MAX_BAY_COUNT);
00448   return HasBit(status, nr);
00449 }
00450 
00456 uint RoadStop::AllocateBay()
00457 {
00458   assert(HasFreeBay());
00459 
00460   /* Find the first free bay. If the bit is set, the bay is free. */
00461   uint bay_nr = 0;
00462   while (!HasBit(status, bay_nr)) bay_nr++;
00463 
00464   ClrBit(status, bay_nr);
00465   return bay_nr;
00466 }
00467 
00472 void RoadStop::AllocateDriveThroughBay(uint nr)
00473 {
00474   assert(nr < MAX_BAY_COUNT);
00475   ClrBit(status, nr);
00476 }
00477 
00482 void RoadStop::FreeBay(uint nr)
00483 {
00484   assert(nr < MAX_BAY_COUNT);
00485   SetBit(status, nr);
00486 }
00487 
00488 
00490 bool RoadStop::IsEntranceBusy() const
00491 {
00492   return HasBit(status, 7);
00493 }
00494 
00496 void RoadStop::SetEntranceBusy(bool busy)
00497 {
00498   SB(status, 7, 1, busy);
00499 }
00500 
00506 RoadStop *RoadStop::GetNextRoadStop(const Vehicle *v) const
00507 {
00508   for (RoadStop *rs = this->next; rs != NULL; rs = rs->next) {
00509     /* The vehicle cannot go to this roadstop (different roadtype) */
00510     if ((GetRoadTypes(rs->xy) & v->u.road.compatible_roadtypes) == ROADTYPES_NONE) continue;
00511     /* The vehicle is articulated and can therefor not go the a standard road stop */
00512     if (IsStandardRoadStopTile(rs->xy) && RoadVehHasArticPart(v)) continue;
00513 
00514     /* The vehicle can actually go to this road stop. So, return it! */
00515     return rs;
00516   }
00517 
00518   return NULL;
00519 }

Generated on Wed Oct 1 17:03:23 2008 for openttd by  doxygen 1.5.6