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 "window_func.h"
00018 #include "date_func.h"
00019 #include "command_func.h"
00020 #include "news_func.h"
00021 #include "aircraft.h"
00022 #include "vehiclelist.h"
00023 #include "core/pool_func.hpp"
00024 #include "station_base.h"
00025 #include "roadstop_base.h"
00026 #include "industry.h"
00027 #include "core/random_func.hpp"
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
00072 Station::~Station()
00073 {
00074 if (CleaningPool()) {
00075 for (CargoID c = 0; c < NUM_CARGO; c++) {
00076 this->goods[c].cargo.OnCleanPool();
00077 }
00078 return;
00079 }
00080
00081 while (!this->loading_vehicles.empty()) {
00082 this->loading_vehicles.front()->LeaveStation();
00083 }
00084
00085 Aircraft *a;
00086 FOR_ALL_AIRCRAFT(a) {
00087 if (!a->IsNormalAircraft()) continue;
00088 if (a->targetairport == this->index) a->targetairport = INVALID_STATION;
00089 }
00090
00091 Vehicle *v;
00092 FOR_ALL_VEHICLES(v) {
00093
00094 if (v->last_station_visited == this->index) {
00095 v->last_station_visited = INVALID_STATION;
00096 }
00097 }
00098
00099
00100 delete this->airport.psa;
00101
00102 if (this->owner == OWNER_NONE) {
00103
00104 InvalidateWindowClassesData(WC_STATION_LIST, 0);
00105 } else {
00106 InvalidateWindowData(WC_STATION_LIST, this->owner, 0);
00107 }
00108
00109 DeleteWindowById(WC_STATION_VIEW, index);
00110
00111
00112 RemoveOrderFromAllVehicles(OT_GOTO_STATION, this->index);
00113
00114
00115 DeleteStationNews(this->index);
00116
00117 for (CargoID c = 0; c < NUM_CARGO; c++) {
00118 this->goods[c].cargo.Truncate(0);
00119 }
00120
00121 CargoPacket::InvalidateAllFrom(this->index);
00122 }
00123
00124
00130 void BaseStation::PostDestructor(size_t index)
00131 {
00132 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
00133 }
00134
00140 RoadStop *Station::GetPrimaryRoadStop(const RoadVehicle *v) const
00141 {
00142 RoadStop *rs = this->GetPrimaryRoadStop(v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK);
00143
00144 for (; rs != NULL; rs = rs->next) {
00145
00146 if ((GetRoadTypes(rs->xy) & v->compatible_roadtypes) == ROADTYPES_NONE) continue;
00147
00148 if (IsStandardRoadStopTile(rs->xy) && v->HasArticulatedPart()) continue;
00149
00150
00151 break;
00152 }
00153
00154 return rs;
00155 }
00156
00161 void Station::AddFacility(StationFacility new_facility_bit, TileIndex facil_xy)
00162 {
00163 if (this->facilities == FACIL_NONE) {
00164 this->xy = facil_xy;
00165 this->random_bits = Random();
00166 }
00167 this->facilities |= new_facility_bit;
00168 this->owner = _current_company;
00169 this->build_date = _date;
00170 }
00171
00177 void Station::MarkTilesDirty(bool cargo_change) const
00178 {
00179 TileIndex tile = this->train_station.tile;
00180 int w, h;
00181
00182 if (tile == INVALID_TILE) return;
00183
00184
00185
00186 if (cargo_change) {
00187
00188
00189
00190 if (this->num_specs == 0) return;
00191 }
00192
00193 for (h = 0; h < train_station.h; h++) {
00194 for (w = 0; w < train_station.w; w++) {
00195 if (this->TileBelongsToRailStation(tile)) {
00196 MarkTileDirtyByTile(tile);
00197 }
00198 tile += TileDiffXY(1, 0);
00199 }
00200 tile += TileDiffXY(-w, 1);
00201 }
00202 }
00203
00204 uint Station::GetPlatformLength(TileIndex tile) const
00205 {
00206 assert(this->TileBelongsToRailStation(tile));
00207
00208 TileIndexDiff delta = (GetRailStationAxis(tile) == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
00209
00210 TileIndex t = tile;
00211 uint len = 0;
00212 do {
00213 t -= delta;
00214 len++;
00215 } while (IsCompatibleTrainStationTile(t, tile));
00216
00217 t = tile;
00218 do {
00219 t += delta;
00220 len++;
00221 } while (IsCompatibleTrainStationTile(t, tile));
00222
00223 return len - 1;
00224 }
00225
00226 uint Station::GetPlatformLength(TileIndex tile, DiagDirection dir) const
00227 {
00228 TileIndex start_tile = tile;
00229 uint length = 0;
00230 assert(IsRailStationTile(tile));
00231 assert(dir < DIAGDIR_END);
00232
00233 do {
00234 length++;
00235 tile += TileOffsByDiagDir(dir);
00236 } while (IsCompatibleTrainStationTile(tile, start_tile));
00237
00238 return length;
00239 }
00240
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_station.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.GetSpec()->catchment);
00255 } else {
00256 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) {
00257 ret = CA_UNMODIFIED;
00258 }
00259 }
00260
00261 return ret;
00262 }
00263
00268 Rect Station::GetCatchmentRect() const
00269 {
00270 assert(!this->rect.IsEmpty());
00271
00272
00273 int catchment_radius = this->GetCatchmentRadius();
00274
00275 Rect ret = {
00276 max<int>(this->rect.left - catchment_radius, 0),
00277 max<int>(this->rect.top - catchment_radius, 0),
00278 min<int>(this->rect.right + catchment_radius, MapMaxX()),
00279 min<int>(this->rect.bottom + catchment_radius, MapMaxY())
00280 };
00281
00282 return ret;
00283 }
00284
00286 struct RectAndIndustryVector {
00287 Rect rect;
00288 IndustryVector *industries_near;
00289 };
00290
00299 static bool FindIndustryToDeliver(TileIndex ind_tile, void *user_data)
00300 {
00301
00302 if (!IsTileType(ind_tile, MP_INDUSTRY)) return false;
00303
00304 RectAndIndustryVector *riv = (RectAndIndustryVector *)user_data;
00305 Industry *ind = Industry::GetByTile(ind_tile);
00306
00307
00308 if (riv->industries_near->Contains(ind)) return false;
00309
00310
00311 int x = TileX(ind_tile);
00312 int y = TileY(ind_tile);
00313 if (x < riv->rect.left || x > riv->rect.right || y < riv->rect.top || y > riv->rect.bottom) return false;
00314
00315
00316 uint cargo_index;
00317 for (cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) {
00318 if (ind->accepts_cargo[cargo_index] != CT_INVALID) break;
00319 }
00320 if (cargo_index >= lengthof(ind->accepts_cargo)) return false;
00321
00322 *riv->industries_near->Append() = ind;
00323
00324 return false;
00325 }
00326
00331 void Station::RecomputeIndustriesNear()
00332 {
00333 this->industries_near.Clear();
00334 if (this->rect.IsEmpty()) return;
00335
00336 RectAndIndustryVector riv = {
00337 this->GetCatchmentRect(),
00338 &this->industries_near
00339 };
00340
00341
00342 TileIndex start_tile = this->xy;
00343 uint max_radius = max(
00344 max(DistanceManhattan(start_tile, TileXY(riv.rect.left, riv.rect.top)), DistanceManhattan(start_tile, TileXY(riv.rect.left, riv.rect.bottom))),
00345 max(DistanceManhattan(start_tile, TileXY(riv.rect.right, riv.rect.top)), DistanceManhattan(start_tile, TileXY(riv.rect.right, riv.rect.bottom)))
00346 );
00347
00348 CircularTileSearch(&start_tile, 2 * max_radius + 1, &FindIndustryToDeliver, &riv);
00349 }
00350
00354 void Station::RecomputeIndustriesNearForAll()
00355 {
00356 Station *st;
00357 FOR_ALL_STATIONS(st) st->RecomputeIndustriesNear();
00358 }
00359
00360
00361
00362
00363
00364 StationRect::StationRect()
00365 {
00366 this->MakeEmpty();
00367 }
00368
00369 void StationRect::MakeEmpty()
00370 {
00371 this->left = this->top = this->right = this->bottom = 0;
00372 }
00373
00383 bool StationRect::PtInExtendedRect(int x, int y, int distance) const
00384 {
00385 return this->left - distance <= x && x <= this->right + distance &&
00386 this->top - distance <= y && y <= this->bottom + distance;
00387 }
00388
00389 bool StationRect::IsEmpty() const
00390 {
00391 return this->left == 0 || this->left > this->right || this->top > this->bottom;
00392 }
00393
00394 CommandCost StationRect::BeforeAddTile(TileIndex tile, StationRectMode mode)
00395 {
00396 int x = TileX(tile);
00397 int y = TileY(tile);
00398 if (this->IsEmpty()) {
00399
00400 if (mode != ADD_TEST) {
00401 this->left = this->right = x;
00402 this->top = this->bottom = y;
00403 }
00404 } else if (!this->PtInExtendedRect(x, y)) {
00405
00406
00407 Rect new_rect = {min(x, this->left), min(y, this->top), max(x, this->right), max(y, this->bottom)};
00408
00409
00410 int w = new_rect.right - new_rect.left + 1;
00411 int h = new_rect.bottom - new_rect.top + 1;
00412 if (mode != ADD_FORCE && (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread)) {
00413 assert(mode != ADD_TRY);
00414 return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT);
00415 }
00416
00417
00418 if (mode != ADD_TEST) {
00419
00420 *this = new_rect;
00421 }
00422 } else {
00423 ;
00424 }
00425 return CommandCost();
00426 }
00427
00428 CommandCost StationRect::BeforeAddRect(TileIndex tile, int w, int h, StationRectMode mode)
00429 {
00430 if (mode == ADD_FORCE || (w <= _settings_game.station.station_spread && h <= _settings_game.station.station_spread)) {
00431
00432 CommandCost ret = this->BeforeAddTile(tile, mode);
00433 if (ret.Succeeded()) ret = this->BeforeAddTile(TILE_ADDXY(tile, w - 1, h - 1), mode);
00434 return ret;
00435 }
00436 return CommandCost();
00437 }
00438
00448 bool StationRect::ScanForStationTiles(StationID st_id, int left_a, int top_a, int right_a, int bottom_a)
00449 {
00450 TileArea ta(TileXY(left_a, top_a), TileXY(right_a, bottom_a));
00451 TILE_AREA_LOOP(tile, ta) {
00452 if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st_id) return true;
00453 }
00454
00455 return false;
00456 }
00457
00458 bool StationRect::AfterRemoveTile(BaseStation *st, TileIndex tile)
00459 {
00460 int x = TileX(tile);
00461 int y = TileY(tile);
00462
00463
00464
00465
00466 for (;;) {
00467
00468 bool left_edge = (x == this->left);
00469 bool right_edge = (x == this->right);
00470 bool top_edge = (y == this->top);
00471 bool bottom_edge = (y == this->bottom);
00472
00473
00474 bool reduce_x = ((left_edge || right_edge) && !ScanForStationTiles(st->index, x, this->top, x, this->bottom));
00475 bool reduce_y = ((top_edge || bottom_edge) && !ScanForStationTiles(st->index, this->left, y, this->right, y));
00476 if (!(reduce_x || reduce_y)) break;
00477
00478 if (reduce_x) {
00479
00480 if (left_edge) {
00481
00482 this->left = x = x + 1;
00483 } else {
00484
00485 this->right = x = x - 1;
00486 }
00487 }
00488 if (reduce_y) {
00489
00490 if (top_edge) {
00491
00492 this->top = y = y + 1;
00493 } else {
00494
00495 this->bottom = y = y - 1;
00496 }
00497 }
00498
00499 if (left > right || top > bottom) {
00500
00501 this->MakeEmpty();
00502 return true;
00503 }
00504 }
00505 return false;
00506 }
00507
00508 bool StationRect::AfterRemoveRect(BaseStation *st, TileArea ta)
00509 {
00510 assert(this->PtInExtendedRect(TileX(ta.tile), TileY(ta.tile)));
00511 assert(this->PtInExtendedRect(TileX(ta.tile) + ta.w - 1, TileY(ta.tile) + ta.h - 1));
00512
00513 bool empty = this->AfterRemoveTile(st, ta.tile);
00514 if (ta.w != 1 || ta.h != 1) empty = empty || this->AfterRemoveTile(st, TILE_ADDXY(ta.tile, ta.w - 1, ta.h - 1));
00515 return empty;
00516 }
00517
00518 StationRect& StationRect::operator = (const Rect &src)
00519 {
00520 this->left = src.left;
00521 this->top = src.top;
00522 this->right = src.right;
00523 this->bottom = src.bottom;
00524 return *this;
00525 }
00526
00532 Money AirportMaintenanceCost(Owner owner)
00533 {
00534 Money total_cost = 0;
00535
00536 const Station *st;
00537 FOR_ALL_STATIONS(st) {
00538 if (st->owner == owner && (st->facilities & FACIL_AIRPORT)) {
00539 total_cost += _price[PR_INFRASTRUCTURE_AIRPORT] * st->airport.GetSpec()->maintenance_cost;
00540 }
00541 }
00542
00543 return total_cost >> 3;
00544 }