00001
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;
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
00097 RemoveOrderFromAllVehicles(OT_GOTO_STATION, index);
00098
00099
00100 DeleteSubsidyWithStation(index);
00101
00102
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
00126 if ((GetRoadTypes(rs->xy) & v->u.road.compatible_roadtypes) == ROADTYPES_NONE) continue;
00127
00128 if (IsStandardRoadStopTile(rs->xy) && RoadVehHasArticPart(v)) continue;
00129
00130
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
00156
00157
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
00174
00175 if (cargo_change) {
00176
00177
00178
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
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
00318 if (mode != ADD_TEST) {
00319 left = right = x;
00320 top = bottom = y;
00321 }
00322 } else if (!PtInExtendedRect(x, y)) {
00323
00324
00325 Rect new_rect = {min(x, left), min(y, top), max(x, right), max(y, bottom)};
00326
00327
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
00337 if (mode != ADD_TEST) {
00338
00339 *this = new_rect;
00340 }
00341 } else {
00342 ;
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)) &&
00350 BeforeAddTile(tile, mode) && BeforeAddTile(TILE_ADDXY(tile, w - 1, h - 1), mode);
00351 }
00352
00362 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
00381
00382
00383 for (;;) {
00384
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
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;
00394
00395 if (reduce_x) {
00396
00397 if (left_edge) {
00398
00399 left = x = x + 1;
00400 } else {
00401
00402 right = x = x - 1;
00403 }
00404 }
00405 if (reduce_y) {
00406
00407 if (top_edge) {
00408
00409 top = y = y + 1;
00410 } else {
00411
00412 bottom = y = y - 1;
00413 }
00414 }
00415
00416 if (left > right || top > bottom) {
00417
00418 MakeEmpty();
00419 return true;
00420 }
00421 }
00422 return false;
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
00447
00448
00450 RoadStop::RoadStop(TileIndex tile) :
00451 xy(tile),
00452 status(3),
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
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
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
00553 if ((GetRoadTypes(rs->xy) & v->u.road.compatible_roadtypes) == ROADTYPES_NONE) continue;
00554
00555 if (IsStandardRoadStopTile(rs->xy) && RoadVehHasArticPart(v)) continue;
00556
00557
00558 return rs;
00559 }
00560
00561 return NULL;
00562 }