station.cpp

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

Generated on Fri Jan 9 19:01:51 2009 for openttd by  doxygen 1.5.6