station.cpp

Go to the documentation of this file.
00001 /* $Id: station.cpp 15718 2009-03-15 00:32:18Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "company_func.h"
00007 #include "industry.h"
00008 #include "newgrf_cargo.h"
00009 #include "yapf/yapf.h"
00010 #include "cargotype.h"
00011 #include "roadveh.h"
00012 #include "window_type.h"
00013 #include "station_gui.h"
00014 #include "zoom_func.h"
00015 #include "functions.h"
00016 #include "window_func.h"
00017 #include "date_func.h"
00018 #include "command_func.h"
00019 #include "news_func.h"
00020 #include "aircraft.h"
00021 #include "vehicle_gui.h"
00022 #include "settings_type.h"
00023 
00024 #include "table/strings.h"
00025 
00026 Station::Station(TileIndex tile)
00027 {
00028   DEBUG(station, cDebugCtorLevel, "I+%3d", index);
00029 
00030   xy = tile;
00031   airport_tile = dock_tile = train_tile = INVALID_TILE;
00032   bus_stops = truck_stops = NULL;
00033   had_vehicle_of_type = 0;
00034   time_since_load = 255;
00035   time_since_unload = 255;
00036   delete_ctr = 0;
00037   facilities = 0;
00038 
00039   last_vehicle_type = VEH_INVALID;
00040   indtype = IT_INVALID;
00041 
00042   random_bits = 0; // Random() must be called when station is really built (DC_EXEC)
00043   waiting_triggers = 0;
00044 }
00045 
00052 Station::~Station()
00053 {
00054   DEBUG(station, cDebugCtorLevel, "I-%3d", index);
00055 
00056   free(this->name);
00057   free(this->speclist);
00058 
00059   if (CleaningPool()) return;
00060 
00061   while (!loading_vehicles.empty()) {
00062     loading_vehicles.front()->LeaveStation();
00063   }
00064 
00065   Vehicle *v;
00066   FOR_ALL_VEHICLES(v) {
00067     if (v->type == VEH_AIRCRAFT && IsNormalAircraft(v) && v->u.air.targetairport == this->index) {
00068       v->u.air.targetairport = INVALID_STATION;
00069     }
00070   }
00071 
00072   MarkDirty();
00073   InvalidateWindowData(WC_STATION_LIST, this->owner, 0);
00074 
00075   DeleteWindowById(WC_STATION_VIEW, index);
00076   WindowNumber wno = (index << 16) | VLW_STATION_LIST | this->owner;
00077   DeleteWindowById(WC_TRAINS_LIST, wno | (VEH_TRAIN << 11));
00078   DeleteWindowById(WC_ROADVEH_LIST, wno | (VEH_ROAD << 11));
00079   DeleteWindowById(WC_SHIPS_LIST, wno | (VEH_SHIP << 11));
00080   DeleteWindowById(WC_AIRCRAFT_LIST, wno | (VEH_AIRCRAFT << 11));
00081 
00082   /* Now delete all orders that go to the station */
00083   RemoveOrderFromAllVehicles(OT_GOTO_STATION, index);
00084 
00085   /* Subsidies need removal as well */
00086   DeleteSubsidyWithStation(index);
00087 
00088   /* Remove all news items */
00089   DeleteStationNews(this->index);
00090 
00091   xy = INVALID_TILE;
00092 
00093   InvalidateWindowData(WC_SELECT_STATION, 0, 0);
00094 
00095   for (CargoID c = 0; c < NUM_CARGO; c++) {
00096     goods[c].cargo.Truncate(0);
00097   }
00098 }
00099 
00100 
00106 RoadStop *Station::GetPrimaryRoadStop(const Vehicle *v) const
00107 {
00108   RoadStop *rs = this->GetPrimaryRoadStop(IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? ROADSTOP_BUS : ROADSTOP_TRUCK);
00109 
00110   for (; rs != NULL; rs = rs->next) {
00111     /* The vehicle cannot go to this roadstop (different roadtype) */
00112     if ((GetRoadTypes(rs->xy) & v->u.road.compatible_roadtypes) == ROADTYPES_NONE) continue;
00113     /* The vehicle is articulated and can therefor not go the a standard road stop */
00114     if (IsStandardRoadStopTile(rs->xy) && RoadVehHasArticPart(v)) continue;
00115 
00116     /* The vehicle can actually go to this road stop. So, return it! */
00117     break;
00118   }
00119 
00120   return rs;
00121 }
00122 
00125 void Station::AddFacility(byte new_facility_bit, TileIndex facil_xy)
00126 {
00127   if (facilities == 0) {
00128     xy = facil_xy;
00129     random_bits = Random();
00130   }
00131   facilities |= new_facility_bit;
00132   owner = _current_company;
00133   build_date = _date;
00134 }
00135 
00136 void Station::MarkDirty() const
00137 {
00138   if (sign.width_1 != 0) {
00139     InvalidateWindowWidget(WC_STATION_VIEW, index, SVW_CAPTION);
00140 
00141     /* We use ZOOM_LVL_MAX here, as every viewport can have an other zoom,
00142      *  and there is no way for us to know which is the biggest. So make the
00143      *  biggest area dirty, and we are safe for sure. */
00144     MarkAllViewportsDirty(
00145       sign.left - 6,
00146       sign.top,
00147       sign.left + ScaleByZoom(sign.width_1 + 12, ZOOM_LVL_MAX),
00148       sign.top + ScaleByZoom(12, ZOOM_LVL_MAX));
00149   }
00150 }
00151 
00152 void Station::MarkTilesDirty(bool cargo_change) const
00153 {
00154   TileIndex tile = train_tile;
00155   int w, h;
00156 
00157   if (tile == INVALID_TILE) return;
00158 
00159   /* cargo_change is set if we're refreshing the tiles due to cargo moving
00160    * around. */
00161   if (cargo_change) {
00162     /* Don't waste time updating if there are no custom station graphics
00163      * that might change. Even if there are custom graphics, they might
00164      * not change. Unfortunately we have no way of telling. */
00165     if (this->num_specs == 0) return;
00166   }
00167 
00168   for (h = 0; h < trainst_h; h++) {
00169     for (w = 0; w < trainst_w; w++) {
00170       if (TileBelongsToRailStation(tile)) {
00171         MarkTileDirtyByTile(tile);
00172       }
00173       tile += TileDiffXY(1, 0);
00174     }
00175     tile += TileDiffXY(-w, 1);
00176   }
00177 }
00178 
00179 bool Station::TileBelongsToRailStation(TileIndex tile) const
00180 {
00181   return IsTileType(tile, MP_STATION) && GetStationIndex(tile) == index && IsRailwayStation(tile);
00182 }
00183 
00189 uint Station::GetPlatformLength(TileIndex tile) const
00190 {
00191   TileIndex t;
00192   TileIndexDiff delta;
00193   uint len = 0;
00194   assert(TileBelongsToRailStation(tile));
00195 
00196   delta = (GetRailStationAxis(tile) == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
00197 
00198   t = tile;
00199   do {
00200     t -= delta;
00201     len++;
00202   } while (IsCompatibleTrainStationTile(t, tile));
00203 
00204   t = tile;
00205   do {
00206     t += delta;
00207     len++;
00208   } while (IsCompatibleTrainStationTile(t, tile));
00209 
00210   return len - 1;
00211 }
00212 
00219 uint Station::GetPlatformLength(TileIndex tile, DiagDirection dir) const
00220 {
00221   TileIndex start_tile = tile;
00222   uint length = 0;
00223   assert(IsRailwayStationTile(tile));
00224   assert(dir < DIAGDIR_END);
00225 
00226   do {
00227     length ++;
00228     tile += TileOffsByDiagDir(dir);
00229   } while (IsCompatibleTrainStationTile(tile, start_tile));
00230 
00231   return length;
00232 }
00233 
00237 bool Station::IsBuoy() const
00238 {
00239   return (had_vehicle_of_type & HVOT_BUOY) != 0;
00240 }
00241 
00245 uint Station::GetCatchmentRadius() const
00246 {
00247   uint ret = CA_NONE;
00248 
00249   if (_settings_game.station.modified_catchment) {
00250     if (this->bus_stops    != NULL)         ret = max<uint>(ret, CA_BUS);
00251     if (this->truck_stops  != NULL)         ret = max<uint>(ret, CA_TRUCK);
00252     if (this->train_tile   != INVALID_TILE) ret = max<uint>(ret, CA_TRAIN);
00253     if (this->dock_tile    != INVALID_TILE) ret = max<uint>(ret, CA_DOCK);
00254     if (this->airport_tile != INVALID_TILE) ret = max<uint>(ret, this->Airport()->catchment);
00255   } else {
00256     if (this->bus_stops != NULL || this->truck_stops != NULL || this->train_tile != INVALID_TILE || this->dock_tile != INVALID_TILE || this->airport_tile != INVALID_TILE) {
00257       ret = CA_UNMODIFIED;
00258     }
00259   }
00260 
00261   return ret;
00262 }
00263 
00264 
00265 /************************************************************************/
00266 /*                     StationRect implementation                       */
00267 /************************************************************************/
00268 
00269 StationRect::StationRect()
00270 {
00271   MakeEmpty();
00272 }
00273 
00274 void StationRect::MakeEmpty()
00275 {
00276   left = top = right = bottom = 0;
00277 }
00278 
00288 bool StationRect::PtInExtendedRect(int x, int y, int distance) const
00289 {
00290   return (left - distance <= x && x <= right + distance && top - distance <= y && y <= bottom + distance);
00291 }
00292 
00293 bool StationRect::IsEmpty() const
00294 {
00295   return (left == 0 || left > right || top > bottom);
00296 }
00297 
00298 bool StationRect::BeforeAddTile(TileIndex tile, StationRectMode mode)
00299 {
00300   int x = TileX(tile);
00301   int y = TileY(tile);
00302   if (IsEmpty()) {
00303     /* we are adding the first station tile */
00304     if (mode != ADD_TEST) {
00305       left = right = x;
00306       top = bottom = y;
00307     }
00308   } else if (!PtInExtendedRect(x, y)) {
00309     /* current rect is not empty and new point is outside this rect
00310      * make new spread-out rectangle */
00311     Rect new_rect = {min(x, left), min(y, top), max(x, right), max(y, bottom)};
00312 
00313     /* check new rect dimensions against preset max */
00314     int w = new_rect.right - new_rect.left + 1;
00315     int h = new_rect.bottom - new_rect.top + 1;
00316     if (mode != ADD_FORCE && (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread)) {
00317       assert(mode != ADD_TRY);
00318       _error_message = STR_306C_STATION_TOO_SPREAD_OUT;
00319       return false;
00320     }
00321 
00322     /* spread-out ok, return true */
00323     if (mode != ADD_TEST) {
00324       /* we should update the station rect */
00325       *this = new_rect;
00326     }
00327   } else {
00328     ; // new point is inside the rect, we don't need to do anything
00329   }
00330   return true;
00331 }
00332 
00333 bool StationRect::BeforeAddRect(TileIndex tile, int w, int h, StationRectMode mode)
00334 {
00335   return (mode == ADD_FORCE || (w <= _settings_game.station.station_spread && h <= _settings_game.station.station_spread)) && // important when the old rect is completely inside the new rect, resp. the old one was empty
00336       BeforeAddTile(tile, mode) && BeforeAddTile(TILE_ADDXY(tile, w - 1, h - 1), mode);
00337 }
00338 
00348 /* static */ bool StationRect::ScanForStationTiles(StationID st_id, int left_a, int top_a, int right_a, int bottom_a)
00349 {
00350   TileIndex top_left = TileXY(left_a, top_a);
00351   int width = right_a - left_a + 1;
00352   int height = bottom_a - top_a + 1;
00353 
00354   BEGIN_TILE_LOOP(tile, width, height, top_left)
00355     if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st_id) return true;
00356   END_TILE_LOOP(tile, width, height, top_left);
00357 
00358   return false;
00359 }
00360 
00361 bool StationRect::AfterRemoveTile(Station *st, TileIndex tile)
00362 {
00363   int x = TileX(tile);
00364   int y = TileY(tile);
00365 
00366   /* look if removed tile was on the bounding rect edge
00367    * and try to reduce the rect by this edge
00368    * do it until we have empty rect or nothing to do */
00369   for (;;) {
00370     /* check if removed tile is on rect edge */
00371     bool left_edge = (x == left);
00372     bool right_edge = (x == right);
00373     bool top_edge = (y == top);
00374     bool bottom_edge = (y == bottom);
00375 
00376     /* can we reduce the rect in either direction? */
00377     bool reduce_x = ((left_edge || right_edge) && !ScanForStationTiles(st->index, x, top, x, bottom));
00378     bool reduce_y = ((top_edge || bottom_edge) && !ScanForStationTiles(st->index, left, y, right, y));
00379     if (!(reduce_x || reduce_y)) break; // nothing to do (can't reduce)
00380 
00381     if (reduce_x) {
00382       /* reduce horizontally */
00383       if (left_edge) {
00384         /* move left edge right */
00385         left = x = x + 1;
00386       } else {
00387         /* move right edge left */
00388         right = x = x - 1;
00389       }
00390     }
00391     if (reduce_y) {
00392       /* reduce vertically */
00393       if (top_edge) {
00394         /* move top edge down */
00395         top = y = y + 1;
00396       } else {
00397         /* move bottom edge up */
00398         bottom = y = y - 1;
00399       }
00400     }
00401 
00402     if (left > right || top > bottom) {
00403       /* can't continue, if the remaining rectangle is empty */
00404       MakeEmpty();
00405       return true; // empty remaining rect
00406     }
00407   }
00408   return false; // non-empty remaining rect
00409 }
00410 
00411 bool StationRect::AfterRemoveRect(Station *st, TileIndex tile, int w, int h)
00412 {
00413   assert(PtInExtendedRect(TileX(tile), TileY(tile)));
00414   assert(PtInExtendedRect(TileX(tile) + w - 1, TileY(tile) + h - 1));
00415 
00416   bool empty = AfterRemoveTile(st, tile);
00417   if (w != 1 || h != 1) empty = empty || AfterRemoveTile(st, TILE_ADDXY(tile, w - 1, h - 1));
00418   return empty;
00419 }
00420 
00421 StationRect& StationRect::operator = (Rect src)
00422 {
00423   left = src.left;
00424   top = src.top;
00425   right = src.right;
00426   bottom = src.bottom;
00427   return *this;
00428 }
00429 
00430 
00431 /************************************************************************/
00432 /*                     RoadStop implementation                          */
00433 /************************************************************************/
00434 
00436 RoadStop::RoadStop(TileIndex tile) :
00437   xy(tile),
00438   status(3), // stop is free
00439   num_vehicles(0),
00440   next(NULL)
00441 {
00442   DEBUG(ms, cDebugCtorLevel,  "I+ at %d[0x%x]", tile, tile);
00443 }
00444 
00448 RoadStop::~RoadStop()
00449 {
00450   if (CleaningPool()) return;
00451 
00452   /* Clear the slot assignment of all vehicles heading for this road stop */
00453   if (num_vehicles != 0) {
00454     Vehicle *v;
00455 
00456     FOR_ALL_VEHICLES(v) {
00457       if (v->type == VEH_ROAD && v->u.road.slot == this) ClearSlot(v);
00458     }
00459   }
00460   assert(num_vehicles == 0);
00461 
00462   DEBUG(ms, cDebugCtorLevel , "I- at %d[0x%x]", xy, xy);
00463 
00464   xy = INVALID_TILE;
00465 }
00466 
00468 bool RoadStop::HasFreeBay() const
00469 {
00470   return GB(status, 0, MAX_BAY_COUNT) != 0;
00471 }
00472 
00474 bool RoadStop::IsFreeBay(uint nr) const
00475 {
00476   assert(nr < MAX_BAY_COUNT);
00477   return HasBit(status, nr);
00478 }
00479 
00485 uint RoadStop::AllocateBay()
00486 {
00487   assert(HasFreeBay());
00488 
00489   /* Find the first free bay. If the bit is set, the bay is free. */
00490   uint bay_nr = 0;
00491   while (!HasBit(status, bay_nr)) bay_nr++;
00492 
00493   ClrBit(status, bay_nr);
00494   return bay_nr;
00495 }
00496 
00501 void RoadStop::AllocateDriveThroughBay(uint nr)
00502 {
00503   assert(nr < MAX_BAY_COUNT);
00504   ClrBit(status, nr);
00505 }
00506 
00511 void RoadStop::FreeBay(uint nr)
00512 {
00513   assert(nr < MAX_BAY_COUNT);
00514   SetBit(status, nr);
00515 }
00516 
00517 
00519 bool RoadStop::IsEntranceBusy() const
00520 {
00521   return HasBit(status, 7);
00522 }
00523 
00525 void RoadStop::SetEntranceBusy(bool busy)
00526 {
00527   SB(status, 7, 1, busy);
00528 }
00529 
00535 RoadStop *RoadStop::GetNextRoadStop(const Vehicle *v) const
00536 {
00537   for (RoadStop *rs = this->next; rs != NULL; rs = rs->next) {
00538     /* The vehicle cannot go to this roadstop (different roadtype) */
00539     if ((GetRoadTypes(rs->xy) & v->u.road.compatible_roadtypes) == ROADTYPES_NONE) continue;
00540     /* The vehicle is articulated and can therefor not go the a standard road stop */
00541     if (IsStandardRoadStopTile(rs->xy) && RoadVehHasArticPart(v)) continue;
00542 
00543     /* The vehicle can actually go to this road stop. So, return it! */
00544     return rs;
00545   }
00546 
00547   return NULL;
00548 }

Generated on Fri Jul 31 22:33:18 2009 for OpenTTD by  doxygen 1.5.6