00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "company_func.h"
00014 #include "company_base.h"
00015 #include "roadveh.h"
00016 #include "viewport_func.h"
00017 #include "date_func.h"
00018 #include "command_func.h"
00019 #include "news_func.h"
00020 #include "aircraft.h"
00021 #include "vehiclelist.h"
00022 #include "core/pool_func.hpp"
00023 #include "station_base.h"
00024 #include "roadstop_base.h"
00025 #include "industry.h"
00026 #include "core/random_func.hpp"
00027 #include "linkgraph/linkgraph.h"
00028
00029 #include "table/strings.h"
00030
00032 StationPool _station_pool("Station");
00033 INSTANTIATE_POOL_METHODS(Station)
00034
00035 BaseStation::~BaseStation()
00036 {
00037 free(this->name);
00038 free(this->speclist);
00039
00040 if (CleaningPool()) return;
00041
00042 Owner owner = this->owner;
00043 if (!Company::IsValidID(owner)) owner = _local_company;
00044 if (!Company::IsValidID(owner)) return;
00045 DeleteWindowById(WC_TRAINS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_TRAIN, owner, this->index).Pack());
00046 DeleteWindowById(WC_ROADVEH_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_ROAD, owner, this->index).Pack());
00047 DeleteWindowById(WC_SHIPS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_SHIP, owner, this->index).Pack());
00048 DeleteWindowById(WC_AIRCRAFT_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_AIRCRAFT, owner, this->index).Pack());
00049
00050 this->sign.MarkDirty();
00051 }
00052
00053 Station::Station(TileIndex tile) :
00054 SpecializedStation<Station, false>(tile),
00055 bus_station(INVALID_TILE, 0, 0),
00056 truck_station(INVALID_TILE, 0, 0),
00057 dock_tile(INVALID_TILE),
00058 indtype(IT_INVALID),
00059 time_since_load(255),
00060 time_since_unload(255),
00061 last_vehicle_type(VEH_INVALID)
00062 {
00063
00064 }
00065
00073 Station::~Station()
00074 {
00075 if (CleaningPool()) {
00076 for (CargoID c = 0; c < NUM_CARGO; c++) {
00077 this->goods[c].cargo.OnCleanPool();
00078 }
00079 return;
00080 }
00081
00082 while (!this->loading_vehicles.empty()) {
00083 this->loading_vehicles.front()->LeaveStation();
00084 }
00085
00086 Aircraft *a;
00087 FOR_ALL_AIRCRAFT(a) {
00088 if (!a->IsNormalAircraft()) continue;
00089 if (a->targetairport == this->index) a->targetairport = INVALID_STATION;
00090 }
00091
00092 for (CargoID c = 0; c < NUM_CARGO; ++c) {
00093 LinkGraph *lg = LinkGraph::GetIfValid(this->goods[c].link_graph);
00094 if (lg != NULL) {
00095 lg->RemoveNode(this->goods[c].node);
00096 if (lg->GetSize() == 0) {
00097 LinkGraphSchedule::Instance()->Unqueue(lg);
00098 delete lg;
00099 }
00100 }
00101 Station *st;
00102 FOR_ALL_STATIONS(st) {
00103 DeleteStaleFlows(st->index, c, this->index);
00104 GoodsEntry *ge = &st->goods[c];
00105 ge->cargo.Reroute(UINT_MAX, &ge->cargo, this->index, st->index, ge);
00106 }
00107 }
00108
00109 Vehicle *v;
00110 FOR_ALL_VEHICLES(v) {
00111
00112 if (v->last_station_visited == this->index) {
00113 v->last_station_visited = INVALID_STATION;
00114 }
00115 if (v->last_loading_station == this->index) {
00116 v->last_loading_station = INVALID_STATION;
00117 }
00118 }
00119
00120
00121 delete this->airport.psa;
00122
00123 if (this->owner == OWNER_NONE) {
00124
00125 InvalidateWindowClassesData(WC_STATION_LIST, 0);
00126 } else {
00127 InvalidateWindowData(WC_STATION_LIST, this->owner, 0);
00128 }
00129
00130 DeleteWindowById(WC_STATION_VIEW, index);
00131
00132
00133 RemoveOrderFromAllVehicles(OT_GOTO_STATION, this->index);
00134
00135
00136 DeleteStationNews(this->index);
00137
00138 for (CargoID c = 0; c < NUM_CARGO; c++) {
00139 this->goods[c].cargo.Truncate();
00140 }
00141
00142 CargoPacket::InvalidateAllFrom(this->index);
00143 }
00144
00145
00151 void BaseStation::PostDestructor(size_t index)
00152 {
00153 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
00154 }
00155
00161 RoadStop *Station::GetPrimaryRoadStop(const RoadVehicle *v) const
00162 {
00163 RoadStop *rs = this->GetPrimaryRoadStop(v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK);
00164
00165 for (; rs != NULL; rs = rs->next) {
00166
00167 if ((GetRoadTypes(rs->xy) & v->compatible_roadtypes) == ROADTYPES_NONE) continue;
00168
00169 if (IsStandardRoadStopTile(rs->xy) && v->HasArticulatedPart()) continue;
00170
00171
00172 break;
00173 }
00174
00175 return rs;
00176 }
00177
00182 void Station::AddFacility(StationFacility new_facility_bit, TileIndex facil_xy)
00183 {
00184 if (this->facilities == FACIL_NONE) {
00185 this->xy = facil_xy;
00186 this->random_bits = Random();
00187 }
00188 this->facilities |= new_facility_bit;
00189 this->owner = _current_company;
00190 this->build_date = _date;
00191 }
00192
00198 void Station::MarkTilesDirty(bool cargo_change) const
00199 {
00200 TileIndex tile = this->train_station.tile;
00201 int w, h;
00202
00203 if (tile == INVALID_TILE) return;
00204
00205
00206
00207 if (cargo_change) {
00208
00209
00210
00211 if (this->num_specs == 0) return;
00212 }
00213
00214 for (h = 0; h < train_station.h; h++) {
00215 for (w = 0; w < train_station.w; w++) {
00216 if (this->TileBelongsToRailStation(tile)) {
00217 MarkTileDirtyByTile(tile);
00218 }
00219 tile += TileDiffXY(1, 0);
00220 }
00221 tile += TileDiffXY(-w, 1);
00222 }
00223 }
00224
00225 uint Station::GetPlatformLength(TileIndex tile) const
00226 {
00227 assert(this->TileBelongsToRailStation(tile));
00228
00229 TileIndexDiff delta = (GetRailStationAxis(tile) == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
00230
00231 TileIndex t = tile;
00232 uint len = 0;
00233 do {
00234 t -= delta;
00235 len++;
00236 } while (IsCompatibleTrainStationTile(t, tile));
00237
00238 t = tile;
00239 do {
00240 t += delta;
00241 len++;
00242 } while (IsCompatibleTrainStationTile(t, tile));
00243
00244 return len - 1;
00245 }
00246
00247 uint Station::GetPlatformLength(TileIndex tile, DiagDirection dir) const
00248 {
00249 TileIndex start_tile = tile;
00250 uint length = 0;
00251 assert(IsRailStationTile(tile));
00252 assert(dir < DIAGDIR_END);
00253
00254 do {
00255 length++;
00256 tile += TileOffsByDiagDir(dir);
00257 } while (IsCompatibleTrainStationTile(tile, start_tile));
00258
00259 return length;
00260 }
00261
00266 uint Station::GetCatchmentRadius() const
00267 {
00268 uint ret = CA_NONE;
00269
00270 if (_settings_game.station.modified_catchment) {
00271 if (this->bus_stops != NULL) ret = max<uint>(ret, CA_BUS);
00272 if (this->truck_stops != NULL) ret = max<uint>(ret, CA_TRUCK);
00273 if (this->train_station.tile != INVALID_TILE) ret = max<uint>(ret, CA_TRAIN);
00274 if (this->dock_tile != INVALID_TILE) ret = max<uint>(ret, CA_DOCK);
00275 if (this->airport.tile != INVALID_TILE) ret = max<uint>(ret, this->airport.GetSpec()->catchment);
00276 } else {
00277 if (this->bus_stops != NULL || this->truck_stops != NULL || this->train_station.tile != INVALID_TILE || this->dock_tile != INVALID_TILE || this->airport.tile != INVALID_TILE) {
00278 ret = CA_UNMODIFIED;
00279 }
00280 }
00281
00282 return ret;
00283 }
00284
00289 Rect Station::GetCatchmentRect() const
00290 {
00291 assert(!this->rect.IsEmpty());
00292
00293
00294 int catchment_radius = this->GetCatchmentRadius();
00295
00296 Rect ret = {
00297 max<int>(this->rect.left - catchment_radius, 0),
00298 max<int>(this->rect.top - catchment_radius, 0),
00299 min<int>(this->rect.right + catchment_radius, MapMaxX()),
00300 min<int>(this->rect.bottom + catchment_radius, MapMaxY())
00301 };
00302
00303 return ret;
00304 }
00305
00307 struct RectAndIndustryVector {
00308 Rect rect;
00309 IndustryVector *industries_near;
00310 };
00311
00320 static bool FindIndustryToDeliver(TileIndex ind_tile, void *user_data)
00321 {
00322
00323 if (!IsTileType(ind_tile, MP_INDUSTRY)) return false;
00324
00325 RectAndIndustryVector *riv = (RectAndIndustryVector *)user_data;
00326 Industry *ind = Industry::GetByTile(ind_tile);
00327
00328
00329 if (riv->industries_near->Contains(ind)) return false;
00330
00331
00332 int x = TileX(ind_tile);
00333 int y = TileY(ind_tile);
00334 if (x < riv->rect.left || x > riv->rect.right || y < riv->rect.top || y > riv->rect.bottom) return false;
00335
00336
00337 uint cargo_index;
00338 for (cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) {
00339 if (ind->accepts_cargo[cargo_index] != CT_INVALID) break;
00340 }
00341 if (cargo_index >= lengthof(ind->accepts_cargo)) return false;
00342
00343 *riv->industries_near->Append() = ind;
00344
00345 return false;
00346 }
00347
00352 void Station::RecomputeIndustriesNear()
00353 {
00354 this->industries_near.Clear();
00355 if (this->rect.IsEmpty()) return;
00356
00357 RectAndIndustryVector riv = {
00358 this->GetCatchmentRect(),
00359 &this->industries_near
00360 };
00361
00362
00363 TileIndex start_tile = this->xy;
00364 uint max_radius = max(
00365 max(DistanceManhattan(start_tile, TileXY(riv.rect.left, riv.rect.top)), DistanceManhattan(start_tile, TileXY(riv.rect.left, riv.rect.bottom))),
00366 max(DistanceManhattan(start_tile, TileXY(riv.rect.right, riv.rect.top)), DistanceManhattan(start_tile, TileXY(riv.rect.right, riv.rect.bottom)))
00367 );
00368
00369 CircularTileSearch(&start_tile, 2 * max_radius + 1, &FindIndustryToDeliver, &riv);
00370 }
00371
00375 void Station::RecomputeIndustriesNearForAll()
00376 {
00377 Station *st;
00378 FOR_ALL_STATIONS(st) st->RecomputeIndustriesNear();
00379 }
00380
00381
00382
00383
00384
00385 StationRect::StationRect()
00386 {
00387 this->MakeEmpty();
00388 }
00389
00390 void StationRect::MakeEmpty()
00391 {
00392 this->left = this->top = this->right = this->bottom = 0;
00393 }
00394
00404 bool StationRect::PtInExtendedRect(int x, int y, int distance) const
00405 {
00406 return this->left - distance <= x && x <= this->right + distance &&
00407 this->top - distance <= y && y <= this->bottom + distance;
00408 }
00409
00410 bool StationRect::IsEmpty() const
00411 {
00412 return this->left == 0 || this->left > this->right || this->top > this->bottom;
00413 }
00414
00415 CommandCost StationRect::BeforeAddTile(TileIndex tile, StationRectMode mode)
00416 {
00417 int x = TileX(tile);
00418 int y = TileY(tile);
00419 if (this->IsEmpty()) {
00420
00421 if (mode != ADD_TEST) {
00422 this->left = this->right = x;
00423 this->top = this->bottom = y;
00424 }
00425 } else if (!this->PtInExtendedRect(x, y)) {
00426
00427
00428 Rect new_rect = {min(x, this->left), min(y, this->top), max(x, this->right), max(y, this->bottom)};
00429
00430
00431 int w = new_rect.right - new_rect.left + 1;
00432 int h = new_rect.bottom - new_rect.top + 1;
00433 if (mode != ADD_FORCE && (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread)) {
00434 assert(mode != ADD_TRY);
00435 return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT);
00436 }
00437
00438
00439 if (mode != ADD_TEST) {
00440
00441 *this = new_rect;
00442 }
00443 } else {
00444 ;
00445 }
00446 return CommandCost();
00447 }
00448
00449 CommandCost StationRect::BeforeAddRect(TileIndex tile, int w, int h, StationRectMode mode)
00450 {
00451 if (mode == ADD_FORCE || (w <= _settings_game.station.station_spread && h <= _settings_game.station.station_spread)) {
00452
00453 CommandCost ret = this->BeforeAddTile(tile, mode);
00454 if (ret.Succeeded()) ret = this->BeforeAddTile(TILE_ADDXY(tile, w - 1, h - 1), mode);
00455 return ret;
00456 }
00457 return CommandCost();
00458 }
00459
00469 bool StationRect::ScanForStationTiles(StationID st_id, int left_a, int top_a, int right_a, int bottom_a)
00470 {
00471 TileArea ta(TileXY(left_a, top_a), TileXY(right_a, bottom_a));
00472 TILE_AREA_LOOP(tile, ta) {
00473 if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st_id) return true;
00474 }
00475
00476 return false;
00477 }
00478
00479 bool StationRect::AfterRemoveTile(BaseStation *st, TileIndex tile)
00480 {
00481 int x = TileX(tile);
00482 int y = TileY(tile);
00483
00484
00485
00486
00487 for (;;) {
00488
00489 bool left_edge = (x == this->left);
00490 bool right_edge = (x == this->right);
00491 bool top_edge = (y == this->top);
00492 bool bottom_edge = (y == this->bottom);
00493
00494
00495 bool reduce_x = ((left_edge || right_edge) && !ScanForStationTiles(st->index, x, this->top, x, this->bottom));
00496 bool reduce_y = ((top_edge || bottom_edge) && !ScanForStationTiles(st->index, this->left, y, this->right, y));
00497 if (!(reduce_x || reduce_y)) break;
00498
00499 if (reduce_x) {
00500
00501 if (left_edge) {
00502
00503 this->left = x = x + 1;
00504 } else {
00505
00506 this->right = x = x - 1;
00507 }
00508 }
00509 if (reduce_y) {
00510
00511 if (top_edge) {
00512
00513 this->top = y = y + 1;
00514 } else {
00515
00516 this->bottom = y = y - 1;
00517 }
00518 }
00519
00520 if (left > right || top > bottom) {
00521
00522 this->MakeEmpty();
00523 return true;
00524 }
00525 }
00526 return false;
00527 }
00528
00529 bool StationRect::AfterRemoveRect(BaseStation *st, TileArea ta)
00530 {
00531 assert(this->PtInExtendedRect(TileX(ta.tile), TileY(ta.tile)));
00532 assert(this->PtInExtendedRect(TileX(ta.tile) + ta.w - 1, TileY(ta.tile) + ta.h - 1));
00533
00534 bool empty = this->AfterRemoveTile(st, ta.tile);
00535 if (ta.w != 1 || ta.h != 1) empty = empty || this->AfterRemoveTile(st, TILE_ADDXY(ta.tile, ta.w - 1, ta.h - 1));
00536 return empty;
00537 }
00538
00539 StationRect& StationRect::operator = (const Rect &src)
00540 {
00541 this->left = src.left;
00542 this->top = src.top;
00543 this->right = src.right;
00544 this->bottom = src.bottom;
00545 return *this;
00546 }
00547
00553 Money AirportMaintenanceCost(Owner owner)
00554 {
00555 Money total_cost = 0;
00556
00557 const Station *st;
00558 FOR_ALL_STATIONS(st) {
00559 if (st->owner == owner && (st->facilities & FACIL_AIRPORT)) {
00560 total_cost += _price[PR_INFRASTRUCTURE_AIRPORT] * st->airport.GetSpec()->maintenance_cost;
00561 }
00562 }
00563
00564 return total_cost >> 3;
00565 }