00001
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;
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
00083 RemoveOrderFromAllVehicles(OT_GOTO_STATION, index);
00084
00085
00086 DeleteSubsidyWithStation(index);
00087
00088
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
00112 if ((GetRoadTypes(rs->xy) & v->u.road.compatible_roadtypes) == ROADTYPES_NONE) continue;
00113
00114 if (IsStandardRoadStopTile(rs->xy) && RoadVehHasArticPart(v)) continue;
00115
00116
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
00142
00143
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
00160
00161 if (cargo_change) {
00162
00163
00164
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
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
00304 if (mode != ADD_TEST) {
00305 left = right = x;
00306 top = bottom = y;
00307 }
00308 } else if (!PtInExtendedRect(x, y)) {
00309
00310
00311 Rect new_rect = {min(x, left), min(y, top), max(x, right), max(y, bottom)};
00312
00313
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
00323 if (mode != ADD_TEST) {
00324
00325 *this = new_rect;
00326 }
00327 } else {
00328 ;
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)) &&
00336 BeforeAddTile(tile, mode) && BeforeAddTile(TILE_ADDXY(tile, w - 1, h - 1), mode);
00337 }
00338
00348 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
00367
00368
00369 for (;;) {
00370
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
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;
00380
00381 if (reduce_x) {
00382
00383 if (left_edge) {
00384
00385 left = x = x + 1;
00386 } else {
00387
00388 right = x = x - 1;
00389 }
00390 }
00391 if (reduce_y) {
00392
00393 if (top_edge) {
00394
00395 top = y = y + 1;
00396 } else {
00397
00398 bottom = y = y - 1;
00399 }
00400 }
00401
00402 if (left > right || top > bottom) {
00403
00404 MakeEmpty();
00405 return true;
00406 }
00407 }
00408 return false;
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
00433
00434
00436 RoadStop::RoadStop(TileIndex tile) :
00437 xy(tile),
00438 status(3),
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
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
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
00539 if ((GetRoadTypes(rs->xy) & v->u.road.compatible_roadtypes) == ROADTYPES_NONE) continue;
00540
00541 if (IsStandardRoadStopTile(rs->xy) && RoadVehHasArticPart(v)) continue;
00542
00543
00544 return rs;
00545 }
00546
00547 return NULL;
00548 }