00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "aircraft.h"
00014 #include "bridge_map.h"
00015 #include "cmd_helper.h"
00016 #include "viewport_func.h"
00017 #include "command_func.h"
00018 #include "town.h"
00019 #include "news_func.h"
00020 #include "train.h"
00021 #include "roadveh.h"
00022 #include "industry.h"
00023 #include "newgrf_cargo.h"
00024 #include "newgrf_debug.h"
00025 #include "newgrf_station.h"
00026 #include "newgrf_canal.h"
00027 #include "pathfinder/yapf/yapf_cache.h"
00028 #include "road_internal.h"
00029 #include "autoslope.h"
00030 #include "water.h"
00031 #include "station_gui.h"
00032 #include "strings_func.h"
00033 #include "clear_func.h"
00034 #include "window_func.h"
00035 #include "date_func.h"
00036 #include "vehicle_func.h"
00037 #include "string_func.h"
00038 #include "animated_tile_func.h"
00039 #include "elrail_func.h"
00040 #include "station_base.h"
00041 #include "roadstop_base.h"
00042 #include "newgrf_railtype.h"
00043 #include "waypoint_base.h"
00044 #include "waypoint_func.h"
00045 #include "pbs.h"
00046 #include "debug.h"
00047 #include "core/random_func.hpp"
00048 #include "company_base.h"
00049 #include "table/airporttile_ids.h"
00050 #include "newgrf_airporttiles.h"
00051 #include "order_backup.h"
00052
00053 #include "table/strings.h"
00054
00061 bool IsHangar(TileIndex t)
00062 {
00063 assert(IsTileType(t, MP_STATION));
00064
00065
00066 if (!IsAirport(t)) return false;
00067
00068 const Station *st = Station::GetByTile(t);
00069 const AirportSpec *as = st->airport.GetSpec();
00070
00071 for (uint i = 0; i < as->nof_depots; i++) {
00072 if (st->airport.GetHangarTile(i) == t) return true;
00073 }
00074
00075 return false;
00076 }
00077
00085 template <class T>
00086 CommandCost GetStationAround(TileArea ta, StationID closest_station, T **st)
00087 {
00088 ta.tile -= TileDiffXY(1, 1);
00089 ta.w += 2;
00090 ta.h += 2;
00091
00092
00093 TILE_AREA_LOOP(tile_cur, ta) {
00094 if (IsTileType(tile_cur, MP_STATION)) {
00095 StationID t = GetStationIndex(tile_cur);
00096 if (!T::IsValidID(t)) continue;
00097
00098 if (closest_station == INVALID_STATION) {
00099 closest_station = t;
00100 } else if (closest_station != t) {
00101 return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
00102 }
00103 }
00104 }
00105 *st = (closest_station == INVALID_STATION) ? NULL : T::Get(closest_station);
00106 return CommandCost();
00107 }
00108
00114 typedef bool (*CMSAMatcher)(TileIndex tile);
00115
00122 static int CountMapSquareAround(TileIndex tile, CMSAMatcher cmp)
00123 {
00124 int num = 0;
00125
00126 for (int dx = -3; dx <= 3; dx++) {
00127 for (int dy = -3; dy <= 3; dy++) {
00128 TileIndex t = TileAddWrap(tile, dx, dy);
00129 if (t != INVALID_TILE && cmp(t)) num++;
00130 }
00131 }
00132
00133 return num;
00134 }
00135
00141 static bool CMSAMine(TileIndex tile)
00142 {
00143
00144 if (!IsTileType(tile, MP_INDUSTRY)) return false;
00145
00146 const Industry *ind = Industry::GetByTile(tile);
00147
00148
00149 if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_EXTRACTIVE) == 0) return false;
00150
00151 for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
00152
00153
00154 if (ind->produced_cargo[i] != CT_INVALID &&
00155 (CargoSpec::Get(ind->produced_cargo[i])->classes & (CC_LIQUID | CC_PASSENGERS | CC_MAIL)) == 0) {
00156 return true;
00157 }
00158 }
00159
00160 return false;
00161 }
00162
00168 static bool CMSAWater(TileIndex tile)
00169 {
00170 return IsTileType(tile, MP_WATER) && IsWater(tile);
00171 }
00172
00178 static bool CMSATree(TileIndex tile)
00179 {
00180 return IsTileType(tile, MP_TREES);
00181 }
00182
00183 #define M(x) ((x) - STR_SV_STNAME)
00184
00185 enum StationNaming {
00186 STATIONNAMING_RAIL,
00187 STATIONNAMING_ROAD,
00188 STATIONNAMING_AIRPORT,
00189 STATIONNAMING_OILRIG,
00190 STATIONNAMING_DOCK,
00191 STATIONNAMING_HELIPORT,
00192 };
00193
00195 struct StationNameInformation {
00196 uint32 free_names;
00197 bool *indtypes;
00198 };
00199
00208 static bool FindNearIndustryName(TileIndex tile, void *user_data)
00209 {
00210
00211 StationNameInformation *sni = (StationNameInformation*)user_data;
00212 if (!IsTileType(tile, MP_INDUSTRY)) return false;
00213
00214
00215 IndustryType indtype = GetIndustryType(tile);
00216 if (GetIndustrySpec(indtype)->station_name == STR_UNDEFINED) return false;
00217
00218
00219
00220 sni->free_names &= ~(1 << M(STR_SV_STNAME_OILFIELD) | 1 << M(STR_SV_STNAME_MINES));
00221 return !sni->indtypes[indtype];
00222 }
00223
00224 static StringID GenerateStationName(Station *st, TileIndex tile, StationNaming name_class)
00225 {
00226 static const uint32 _gen_station_name_bits[] = {
00227 0,
00228 0,
00229 1U << M(STR_SV_STNAME_AIRPORT),
00230 1U << M(STR_SV_STNAME_OILFIELD),
00231 1U << M(STR_SV_STNAME_DOCKS),
00232 1U << M(STR_SV_STNAME_HELIPORT),
00233 };
00234
00235 const Town *t = st->town;
00236 uint32 free_names = UINT32_MAX;
00237
00238 bool indtypes[NUM_INDUSTRYTYPES];
00239 memset(indtypes, 0, sizeof(indtypes));
00240
00241 const Station *s;
00242 FOR_ALL_STATIONS(s) {
00243 if (s != st && s->town == t) {
00244 if (s->indtype != IT_INVALID) {
00245 indtypes[s->indtype] = true;
00246 continue;
00247 }
00248 uint str = M(s->string_id);
00249 if (str <= 0x20) {
00250 if (str == M(STR_SV_STNAME_FOREST)) {
00251 str = M(STR_SV_STNAME_WOODS);
00252 }
00253 ClrBit(free_names, str);
00254 }
00255 }
00256 }
00257
00258 TileIndex indtile = tile;
00259 StationNameInformation sni = { free_names, indtypes };
00260 if (CircularTileSearch(&indtile, 7, FindNearIndustryName, &sni)) {
00261
00262 IndustryType indtype = GetIndustryType(indtile);
00263 const IndustrySpec *indsp = GetIndustrySpec(indtype);
00264
00265 if (indsp->station_name != STR_NULL) {
00266 st->indtype = indtype;
00267 return STR_SV_STNAME_FALLBACK;
00268 }
00269 }
00270
00271
00272 free_names = sni.free_names;
00273
00274
00275 uint32 tmp = free_names & _gen_station_name_bits[name_class];
00276 if (tmp != 0) return STR_SV_STNAME + FindFirstBit(tmp);
00277
00278
00279 if (HasBit(free_names, M(STR_SV_STNAME_MINES))) {
00280 if (CountMapSquareAround(tile, CMSAMine) >= 2) {
00281 return STR_SV_STNAME_MINES;
00282 }
00283 }
00284
00285
00286 if (DistanceMax(tile, t->xy) < 8) {
00287 if (HasBit(free_names, M(STR_SV_STNAME))) return STR_SV_STNAME;
00288
00289 if (HasBit(free_names, M(STR_SV_STNAME_CENTRAL))) return STR_SV_STNAME_CENTRAL;
00290 }
00291
00292
00293 if (HasBit(free_names, M(STR_SV_STNAME_LAKESIDE)) &&
00294 DistanceFromEdge(tile) < 20 &&
00295 CountMapSquareAround(tile, CMSAWater) >= 5) {
00296 return STR_SV_STNAME_LAKESIDE;
00297 }
00298
00299
00300 if (HasBit(free_names, M(STR_SV_STNAME_WOODS)) && (
00301 CountMapSquareAround(tile, CMSATree) >= 8 ||
00302 CountMapSquareAround(tile, IsTileForestIndustry) >= 2)
00303 ) {
00304 return _settings_game.game_creation.landscape == LT_TROPIC ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
00305 }
00306
00307
00308 uint z = GetTileZ(tile);
00309 uint z2 = GetTileZ(t->xy);
00310 if (z < z2) {
00311 if (HasBit(free_names, M(STR_SV_STNAME_VALLEY))) return STR_SV_STNAME_VALLEY;
00312 } else if (z > z2) {
00313 if (HasBit(free_names, M(STR_SV_STNAME_HEIGHTS))) return STR_SV_STNAME_HEIGHTS;
00314 }
00315
00316
00317 static const int8 _direction_and_table[] = {
00318 ~( (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00319 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00320 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00321 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) ),
00322 };
00323
00324 free_names &= _direction_and_table[
00325 (TileX(tile) < TileX(t->xy)) +
00326 (TileY(tile) < TileY(t->xy)) * 2];
00327
00328 tmp = free_names & ((1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 6) | (1 << 7) | (1 << 12) | (1 << 26) | (1 << 27) | (1 << 28) | (1 << 29) | (1 << 30));
00329 return (tmp == 0) ? STR_SV_STNAME_FALLBACK : (STR_SV_STNAME + FindFirstBit(tmp));
00330 }
00331 #undef M
00332
00338 static Station *GetClosestDeletedStation(TileIndex tile)
00339 {
00340 uint threshold = 8;
00341 Station *best_station = NULL;
00342 Station *st;
00343
00344 FOR_ALL_STATIONS(st) {
00345 if (!st->IsInUse() && st->owner == _current_company) {
00346 uint cur_dist = DistanceManhattan(tile, st->xy);
00347
00348 if (cur_dist < threshold) {
00349 threshold = cur_dist;
00350 best_station = st;
00351 }
00352 }
00353 }
00354
00355 return best_station;
00356 }
00357
00358
00359 void Station::GetTileArea(TileArea *ta, StationType type) const
00360 {
00361 switch (type) {
00362 case STATION_RAIL:
00363 *ta = this->train_station;
00364 return;
00365
00366 case STATION_AIRPORT:
00367 *ta = this->airport;
00368 return;
00369
00370 case STATION_TRUCK:
00371 *ta = this->truck_station;
00372 return;
00373
00374 case STATION_BUS:
00375 *ta = this->bus_station;
00376 return;
00377
00378 case STATION_DOCK:
00379 case STATION_OILRIG:
00380 ta->tile = this->dock_tile;
00381 break;
00382
00383 default: NOT_REACHED();
00384 }
00385
00386 ta->w = 1;
00387 ta->h = 1;
00388 }
00389
00393 void Station::UpdateVirtCoord()
00394 {
00395 Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE);
00396
00397 pt.y -= 32;
00398 if ((this->facilities & FACIL_AIRPORT) && this->airport.type == AT_OILRIG) pt.y -= 16;
00399
00400 SetDParam(0, this->index);
00401 SetDParam(1, this->facilities);
00402 this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_STATION);
00403
00404 SetWindowDirty(WC_STATION_VIEW, this->index);
00405 }
00406
00408 void UpdateAllStationVirtCoords()
00409 {
00410 BaseStation *st;
00411
00412 FOR_ALL_BASE_STATIONS(st) {
00413 st->UpdateVirtCoord();
00414 }
00415 }
00416
00422 static uint GetAcceptanceMask(const Station *st)
00423 {
00424 uint mask = 0;
00425
00426 for (CargoID i = 0; i < NUM_CARGO; i++) {
00427 if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) mask |= 1 << i;
00428 }
00429 return mask;
00430 }
00431
00436 static void ShowRejectOrAcceptNews(const Station *st, uint num_items, CargoID *cargo, StringID msg)
00437 {
00438 for (uint i = 0; i < num_items; i++) {
00439 SetDParam(i + 1, CargoSpec::Get(cargo[i])->name);
00440 }
00441
00442 SetDParam(0, st->index);
00443 AddNewsItem(msg, NS_ACCEPTANCE, NR_STATION, st->index);
00444 }
00445
00453 CargoArray GetProductionAroundTiles(TileIndex tile, int w, int h, int rad)
00454 {
00455 CargoArray produced;
00456
00457 int x = TileX(tile);
00458 int y = TileY(tile);
00459
00460
00461
00462 int x2 = min(x + w + rad, MapSizeX());
00463 int x1 = max(x - rad, 0);
00464
00465 int y2 = min(y + h + rad, MapSizeY());
00466 int y1 = max(y - rad, 0);
00467
00468 assert(x1 < x2);
00469 assert(y1 < y2);
00470 assert(w > 0);
00471 assert(h > 0);
00472
00473 TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
00474
00475
00476
00477 TILE_AREA_LOOP(tile, ta) AddProducedCargo(tile, produced);
00478
00479
00480
00481
00482
00483
00484
00485 const Industry *i;
00486 FOR_ALL_INDUSTRIES(i) {
00487 if (!ta.Intersects(i->location)) continue;
00488
00489 for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
00490 CargoID cargo = i->produced_cargo[j];
00491 if (cargo != CT_INVALID) produced[cargo]++;
00492 }
00493 }
00494
00495 return produced;
00496 }
00497
00506 CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, uint32 *always_accepted)
00507 {
00508 CargoArray acceptance;
00509 if (always_accepted != NULL) *always_accepted = 0;
00510
00511 int x = TileX(tile);
00512 int y = TileY(tile);
00513
00514
00515
00516 int x2 = min(x + w + rad, MapSizeX());
00517 int y2 = min(y + h + rad, MapSizeY());
00518 int x1 = max(x - rad, 0);
00519 int y1 = max(y - rad, 0);
00520
00521 assert(x1 < x2);
00522 assert(y1 < y2);
00523 assert(w > 0);
00524 assert(h > 0);
00525
00526 for (int yc = y1; yc != y2; yc++) {
00527 for (int xc = x1; xc != x2; xc++) {
00528 TileIndex tile = TileXY(xc, yc);
00529 AddAcceptedCargo(tile, acceptance, always_accepted);
00530 }
00531 }
00532
00533 return acceptance;
00534 }
00535
00541 void UpdateStationAcceptance(Station *st, bool show_msg)
00542 {
00543
00544 uint old_acc = GetAcceptanceMask(st);
00545
00546
00547 CargoArray acceptance;
00548 if (!st->rect.IsEmpty()) {
00549 acceptance = GetAcceptanceAroundTiles(
00550 TileXY(st->rect.left, st->rect.top),
00551 st->rect.right - st->rect.left + 1,
00552 st->rect.bottom - st->rect.top + 1,
00553 st->GetCatchmentRadius(),
00554 &st->always_accepted
00555 );
00556 }
00557
00558
00559 for (CargoID i = 0; i < NUM_CARGO; i++) {
00560 uint amt = min(acceptance[i], 15);
00561
00562
00563 bool is_passengers = IsCargoInClass(i, CC_PASSENGERS);
00564 if ((!is_passengers && !(st->facilities & ~FACIL_BUS_STOP)) ||
00565 (is_passengers && !(st->facilities & ~FACIL_TRUCK_STOP))) {
00566 amt = 0;
00567 }
00568
00569 SB(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, amt >= 8);
00570 }
00571
00572
00573 uint new_acc = GetAcceptanceMask(st);
00574 if (old_acc == new_acc) return;
00575
00576
00577 if (show_msg && st->owner == _local_company && st->IsInUse()) {
00578
00579
00580 static const StringID accept_msg[] = {
00581 STR_NEWS_STATION_NOW_ACCEPTS_CARGO,
00582 STR_NEWS_STATION_NOW_ACCEPTS_CARGO_AND_CARGO,
00583 };
00584 static const StringID reject_msg[] = {
00585 STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO,
00586 STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_OR_CARGO,
00587 };
00588
00589
00590 CargoID accepts[2] = { CT_INVALID, CT_INVALID };
00591 CargoID rejects[2] = { CT_INVALID, CT_INVALID };
00592 uint num_acc = 0;
00593 uint num_rej = 0;
00594
00595
00596 for (CargoID i = 0; i < NUM_CARGO; i++) {
00597 if (HasBit(new_acc, i)) {
00598 if (!HasBit(old_acc, i) && num_acc < lengthof(accepts)) {
00599
00600 accepts[num_acc++] = i;
00601 }
00602 } else {
00603 if (HasBit(old_acc, i) && num_rej < lengthof(rejects)) {
00604
00605 rejects[num_rej++] = i;
00606 }
00607 }
00608 }
00609
00610
00611 if (num_acc > 0) ShowRejectOrAcceptNews(st, num_acc, accepts, accept_msg[num_acc - 1]);
00612 if (num_rej > 0) ShowRejectOrAcceptNews(st, num_rej, rejects, reject_msg[num_rej - 1]);
00613 }
00614
00615
00616 SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_ACCEPTLIST);
00617 }
00618
00619 static void UpdateStationSignCoord(BaseStation *st)
00620 {
00621 const StationRect *r = &st->rect;
00622
00623 if (r->IsEmpty()) return;
00624
00625
00626 st->xy = TileXY(ClampU(TileX(st->xy), r->left, r->right), ClampU(TileY(st->xy), r->top, r->bottom));
00627 st->UpdateVirtCoord();
00628 }
00629
00636 static void DeleteStationIfEmpty(BaseStation *st)
00637 {
00638 if (!st->IsInUse()) {
00639 st->delete_ctr = 0;
00640 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
00641 }
00642
00643 UpdateStationSignCoord(st);
00644 }
00645
00646 CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags);
00647
00656 CommandCost CheckBuildableTile(TileIndex tile, uint invalid_dirs, int &allowed_z, bool check_bridge = true)
00657 {
00658 if (check_bridge && MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) {
00659 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00660 }
00661
00662 CommandCost ret = EnsureNoVehicleOnGround(tile);
00663 if (ret.Failed()) return ret;
00664
00665 uint z;
00666 Slope tileh = GetTileSlope(tile, &z);
00667
00668
00669
00670
00671
00672 if (IsSteepSlope(tileh) ||
00673 ((!_settings_game.construction.build_on_slopes) && tileh != SLOPE_FLAT)) {
00674 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
00675 }
00676
00677 CommandCost cost(EXPENSES_CONSTRUCTION);
00678 int flat_z = z;
00679 if (tileh != SLOPE_FLAT) {
00680
00681 if ((HasBit(invalid_dirs, DIAGDIR_NE) && !(tileh & SLOPE_NE)) ||
00682 (HasBit(invalid_dirs, DIAGDIR_SE) && !(tileh & SLOPE_SE)) ||
00683 (HasBit(invalid_dirs, DIAGDIR_SW) && !(tileh & SLOPE_SW)) ||
00684 (HasBit(invalid_dirs, DIAGDIR_NW) && !(tileh & SLOPE_NW))) {
00685 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
00686 }
00687 cost.AddCost(_price[PR_BUILD_FOUNDATION]);
00688 flat_z += TILE_HEIGHT;
00689 }
00690
00691
00692 if (allowed_z < 0) {
00693
00694 allowed_z = flat_z;
00695 } else if (allowed_z != flat_z) {
00696 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
00697 }
00698
00699 return cost;
00700 }
00701
00708 CommandCost CheckFlatLand(TileArea tile_area, DoCommandFlag flags)
00709 {
00710 CommandCost cost(EXPENSES_CONSTRUCTION);
00711 int allowed_z = -1;
00712
00713 TILE_AREA_LOOP(tile_cur, tile_area) {
00714 CommandCost ret = CheckBuildableTile(tile_cur, 0, allowed_z);
00715 if (ret.Failed()) return ret;
00716 cost.AddCost(ret);
00717
00718 ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00719 if (ret.Failed()) return ret;
00720 cost.AddCost(ret);
00721 }
00722
00723 return cost;
00724 }
00725
00736 static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, StationID *station, RailType rt, SmallVector<Train *, 4> &affected_vehicles)
00737 {
00738 CommandCost cost(EXPENSES_CONSTRUCTION);
00739 int allowed_z = -1;
00740
00741 TILE_AREA_LOOP(tile_cur, tile_area) {
00742 CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z);
00743 if (ret.Failed()) return ret;
00744 cost.AddCost(ret);
00745
00746
00747
00748
00749 if (station != NULL && IsTileType(tile_cur, MP_STATION)) {
00750 if (!IsRailStation(tile_cur)) {
00751 return ClearTile_Station(tile_cur, DC_AUTO);
00752 } else {
00753 StationID st = GetStationIndex(tile_cur);
00754 if (*station == INVALID_STATION) {
00755 *station = st;
00756 } else if (*station != st) {
00757 return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
00758 }
00759 }
00760 } else {
00761
00762
00763 if (rt != INVALID_RAILTYPE &&
00764 IsPlainRailTile(tile_cur) && !HasSignals(tile_cur) &&
00765 HasPowerOnRail(GetRailType(tile_cur), rt)) {
00766
00767
00768
00769
00770
00771
00772 TrackBits tracks = GetTrackBits(tile_cur);
00773 Track track = RemoveFirstTrack(&tracks);
00774 Track expected_track = HasBit(invalid_dirs, DIAGDIR_NE) ? TRACK_X : TRACK_Y;
00775
00776 if (tracks == TRACK_BIT_NONE && track == expected_track) {
00777
00778 if (HasBit(GetRailReservationTrackBits(tile_cur), track)) {
00779 Train *v = GetTrainForReservation(tile_cur, track);
00780 if (v != NULL) {
00781 *affected_vehicles.Append() = v;
00782 }
00783 }
00784 CommandCost ret = DoCommand(tile_cur, 0, track, flags, CMD_REMOVE_SINGLE_RAIL);
00785 if (ret.Failed()) return ret;
00786 cost.AddCost(ret);
00787
00788 continue;
00789 }
00790 }
00791 ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00792 if (ret.Failed()) return ret;
00793 cost.AddCost(ret);
00794 }
00795 }
00796
00797 return cost;
00798 }
00799
00812 static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, bool is_truck_stop, Axis axis, StationID *station, RoadTypes rts)
00813 {
00814 CommandCost cost(EXPENSES_CONSTRUCTION);
00815 int allowed_z = -1;
00816
00817 TILE_AREA_LOOP(cur_tile, tile_area) {
00818 CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z);
00819 if (ret.Failed()) return ret;
00820 cost.AddCost(ret);
00821
00822
00823
00824
00825 if (station != NULL && IsTileType(cur_tile, MP_STATION)) {
00826 if (!IsRoadStop(cur_tile)) {
00827 return ClearTile_Station(cur_tile, DC_AUTO);
00828 } else {
00829 if (is_truck_stop != IsTruckStop(cur_tile) ||
00830 is_drive_through != IsDriveThroughStopTile(cur_tile)) {
00831 return ClearTile_Station(cur_tile, DC_AUTO);
00832 }
00833
00834 if (is_drive_through && IsDriveThroughStopTile(cur_tile) && DiagDirToAxis(GetRoadStopDir(cur_tile)) != axis){
00835 return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
00836 }
00837 StationID st = GetStationIndex(cur_tile);
00838 if (*station == INVALID_STATION) {
00839 *station = st;
00840 } else if (*station != st) {
00841 return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
00842 }
00843 }
00844 } else {
00845 bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
00846
00847 RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
00848 if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
00849
00850 switch (CountBits(rb)) {
00851 case 1:
00852 return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
00853
00854 case 2:
00855 if (rb == ROAD_X || rb == ROAD_Y) return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
00856 return_cmd_error(STR_ERROR_DRIVE_THROUGH_CORNER);
00857
00858 default:
00859 return_cmd_error(STR_ERROR_DRIVE_THROUGH_JUNCTION);
00860 }
00861 }
00862
00863 RoadTypes cur_rts = IsNormalRoadTile(cur_tile) ? GetRoadTypes(cur_tile) : ROADTYPES_NONE;
00864 uint num_roadbits = 0;
00865 if (build_over_road) {
00866
00867 if (HasBit(cur_rts, ROADTYPE_ROAD)) {
00868 Owner road_owner = GetRoadOwner(cur_tile, ROADTYPE_ROAD);
00869 if (road_owner == OWNER_TOWN) {
00870 if (!_settings_game.construction.road_stop_on_town_road) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
00871 } else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) {
00872 CommandCost ret = CheckOwnership(road_owner);
00873 if (ret.Failed()) return ret;
00874 }
00875 num_roadbits += CountBits(GetRoadBits(cur_tile, ROADTYPE_ROAD));
00876 }
00877
00878
00879 if (HasBit(cur_rts, ROADTYPE_TRAM)) {
00880 Owner tram_owner = GetRoadOwner(cur_tile, ROADTYPE_TRAM);
00881 if (!_settings_game.construction.road_stop_on_competitor_road && tram_owner != OWNER_NONE) {
00882 CommandCost ret = CheckOwnership(tram_owner);
00883 if (ret.Failed()) return ret;
00884 }
00885 num_roadbits += CountBits(GetRoadBits(cur_tile, ROADTYPE_TRAM));
00886 }
00887
00888
00889 rts |= cur_rts;
00890 } else {
00891 ret = DoCommand(cur_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00892 if (ret.Failed()) return ret;
00893 cost.AddCost(ret);
00894 }
00895
00896 uint roadbits_to_build = CountBits(rts) * 2 - num_roadbits;
00897 cost.AddCost(_price[PR_BUILD_ROAD] * roadbits_to_build);
00898 }
00899 }
00900
00901 return cost;
00902 }
00903
00911 CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta, Axis axis)
00912 {
00913 TileArea cur_ta = st->train_station;
00914
00915
00916 int x = min(TileX(cur_ta.tile), TileX(new_ta.tile));
00917 int y = min(TileY(cur_ta.tile), TileY(new_ta.tile));
00918 new_ta.w = max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
00919 new_ta.h = max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
00920 new_ta.tile = TileXY(x, y);
00921
00922
00923 if (new_ta.w > _settings_game.station.station_spread || new_ta.h > _settings_game.station.station_spread) {
00924 return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT);
00925 }
00926
00927 return CommandCost();
00928 }
00929
00930 static inline byte *CreateSingle(byte *layout, int n)
00931 {
00932 int i = n;
00933 do *layout++ = 0; while (--i);
00934 layout[((n - 1) >> 1) - n] = 2;
00935 return layout;
00936 }
00937
00938 static inline byte *CreateMulti(byte *layout, int n, byte b)
00939 {
00940 int i = n;
00941 do *layout++ = b; while (--i);
00942 if (n > 4) {
00943 layout[0 - n] = 0;
00944 layout[n - 1 - n] = 0;
00945 }
00946 return layout;
00947 }
00948
00949 void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec)
00950 {
00951 if (statspec != NULL && statspec->lengths >= plat_len &&
00952 statspec->platforms[plat_len - 1] >= numtracks &&
00953 statspec->layouts[plat_len - 1][numtracks - 1]) {
00954
00955 memcpy(layout, statspec->layouts[plat_len - 1][numtracks - 1],
00956 plat_len * numtracks);
00957 return;
00958 }
00959
00960 if (plat_len == 1) {
00961 CreateSingle(layout, numtracks);
00962 } else {
00963 if (numtracks & 1) layout = CreateSingle(layout, plat_len);
00964 numtracks >>= 1;
00965
00966 while (--numtracks >= 0) {
00967 layout = CreateMulti(layout, plat_len, 4);
00968 layout = CreateMulti(layout, plat_len, 6);
00969 }
00970 }
00971 }
00972
00984 template <class T, StringID error_message>
00985 CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st)
00986 {
00987 assert(*st == NULL);
00988 bool check_surrounding = true;
00989
00990 if (_settings_game.station.adjacent_stations) {
00991 if (existing_station != INVALID_STATION) {
00992 if (adjacent && existing_station != station_to_join) {
00993
00994
00995 return_cmd_error(error_message);
00996 } else {
00997
00998
00999 *st = T::GetIfValid(existing_station);
01000 check_surrounding = (*st == NULL);
01001 }
01002 } else {
01003
01004
01005 if (adjacent) check_surrounding = false;
01006 }
01007 }
01008
01009 if (check_surrounding) {
01010
01011 CommandCost ret = GetStationAround(ta, existing_station, st);
01012 if (ret.Failed()) return ret;
01013 }
01014
01015
01016 if (*st == NULL && station_to_join != INVALID_STATION) *st = T::GetIfValid(station_to_join);
01017
01018 return CommandCost();
01019 }
01020
01030 static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
01031 {
01032 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st);
01033 }
01034
01044 CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp)
01045 {
01046 return FindJoiningBaseStation<Waypoint, STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp);
01047 }
01048
01066 CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01067 {
01068
01069 RailType rt = Extract<RailType, 0, 4>(p1);
01070 Axis axis = Extract<Axis, 4, 1>(p1);
01071 byte numtracks = GB(p1, 8, 8);
01072 byte plat_len = GB(p1, 16, 8);
01073 bool adjacent = HasBit(p1, 24);
01074
01075 StationClassID spec_class = Extract<StationClassID, 0, 8>(p2);
01076 byte spec_index = GB(p2, 8, 8);
01077 StationID station_to_join = GB(p2, 16, 16);
01078
01079
01080 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile_org, flags);
01081 if (ret.Failed()) return ret;
01082
01083 if (!ValParamRailtype(rt)) return CMD_ERROR;
01084
01085
01086 if ((uint)spec_class >= StationClass::GetCount() || spec_class == STAT_CLASS_WAYP) return CMD_ERROR;
01087 if (spec_index >= StationClass::GetCount(spec_class)) return CMD_ERROR;
01088 if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
01089
01090 int w_org, h_org;
01091 if (axis == AXIS_X) {
01092 w_org = plat_len;
01093 h_org = numtracks;
01094 } else {
01095 h_org = plat_len;
01096 w_org = numtracks;
01097 }
01098
01099 bool reuse = (station_to_join != NEW_STATION);
01100 if (!reuse) station_to_join = INVALID_STATION;
01101 bool distant_join = (station_to_join != INVALID_STATION);
01102
01103 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
01104
01105 if (h_org > _settings_game.station.station_spread || w_org > _settings_game.station.station_spread) return CMD_ERROR;
01106
01107
01108 TileArea new_location(tile_org, w_org, h_org);
01109
01110
01111 StationID est = INVALID_STATION;
01112 SmallVector<Train *, 4> affected_vehicles;
01113
01114 CommandCost cost = CheckFlatLandRailStation(TileArea(tile_org, w_org, h_org), flags, 5 << axis, &est, rt, affected_vehicles);
01115 if (cost.Failed()) return cost;
01116
01117 cost.AddCost((numtracks * _price[PR_BUILD_STATION_RAIL] + _price[PR_BUILD_STATION_RAIL_LENGTH]) * plat_len);
01118 cost.AddCost(numtracks * plat_len * RailBuildCost(rt));
01119
01120 Station *st = NULL;
01121 ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
01122 if (ret.Failed()) return ret;
01123
01124
01125 if (st == NULL && reuse) st = GetClosestDeletedStation(tile_org);
01126
01127 if (st != NULL) {
01128
01129 if (st->owner != _current_company) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
01130
01131 if (st->train_station.tile != INVALID_TILE) {
01132 CommandCost ret = CanExpandRailStation(st, new_location, axis);
01133 if (ret.Failed()) return ret;
01134 }
01135
01136
01137 CommandCost ret = st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TEST);
01138 if (ret.Failed()) return ret;
01139 } else {
01140
01141 if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
01142
01143 if (flags & DC_EXEC) {
01144 st = new Station(tile_org);
01145
01146 st->town = ClosestTownFromTile(tile_org, UINT_MAX);
01147 st->string_id = GenerateStationName(st, tile_org, STATIONNAMING_RAIL);
01148
01149 if (Company::IsValidID(_current_company)) {
01150 SetBit(st->town->have_ratings, _current_company);
01151 }
01152 }
01153 }
01154
01155
01156 const StationSpec *statspec = StationClass::Get(spec_class, spec_index);
01157 int specindex = AllocateSpecToStation(statspec, st, (flags & DC_EXEC) != 0);
01158 if (specindex == -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS);
01159
01160 if (statspec != NULL) {
01161
01162
01163
01164 if (HasBit(statspec->disallowed_platforms, numtracks - 1) || HasBit(statspec->disallowed_lengths, plat_len - 1)) {
01165 return CMD_ERROR;
01166 }
01167
01168
01169 if (HasBit(statspec->callback_mask, CBM_STATION_AVAIL) && GB(GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE), 0, 8) == 0) {
01170 return CMD_ERROR;
01171 }
01172 }
01173
01174 if (flags & DC_EXEC) {
01175 TileIndexDiff tile_delta;
01176 byte *layout_ptr;
01177 byte numtracks_orig;
01178 Track track;
01179
01180 st->train_station = new_location;
01181 st->AddFacility(FACIL_TRAIN, new_location.tile);
01182
01183 st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
01184
01185 if (statspec != NULL) {
01186
01187
01188 st->cached_anim_triggers |= statspec->animation.triggers;
01189 }
01190
01191 tile_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
01192 track = AxisToTrack(axis);
01193
01194 layout_ptr = AllocaM(byte, numtracks * plat_len);
01195 GetStationLayout(layout_ptr, numtracks, plat_len, statspec);
01196
01197 numtracks_orig = numtracks;
01198
01199 do {
01200 TileIndex tile = tile_org;
01201 int w = plat_len;
01202 do {
01203 byte layout = *layout_ptr++;
01204 if (IsRailStationTile(tile) && HasStationReservation(tile)) {
01205
01206 Train *v = GetTrainForReservation(tile, AxisToTrack(GetRailStationAxis(tile)));
01207 if (v != NULL) {
01208 FreeTrainTrackReservation(v);
01209 *affected_vehicles.Append() = v;
01210 if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), false);
01211 for (; v->Next() != NULL; v = v->Next()) { }
01212 if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), false);
01213 }
01214 }
01215
01216
01217 DeleteAnimatedTile(tile);
01218 byte old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
01219 MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, rt);
01220
01221 DeallocateSpecFromStation(st, old_specindex);
01222
01223 SetCustomStationSpecIndex(tile, specindex);
01224 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
01225 SetAnimationFrame(tile, 0);
01226
01227 if (statspec != NULL) {
01228
01229 uint32 platinfo = GetPlatformInfo(AXIS_X, 0, plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
01230
01231
01232 uint16 callback = GetStationCallback(CBID_STATION_TILE_LAYOUT, platinfo, 0, statspec, NULL, tile);
01233 if (callback != CALLBACK_FAILED && callback < 8) SetStationGfx(tile, (callback & ~1) + axis);
01234
01235
01236 TriggerStationAnimation(st, tile, SAT_BUILT);
01237 }
01238
01239 tile += tile_delta;
01240 } while (--w);
01241 AddTrackToSignalBuffer(tile_org, track, _current_company);
01242 YapfNotifyTrackLayoutChange(tile_org, track);
01243 tile_org += tile_delta ^ TileDiffXY(1, 1);
01244 } while (--numtracks);
01245
01246 for (uint i = 0; i < affected_vehicles.Length(); ++i) {
01247
01248 Train *v = affected_vehicles[i];
01249 if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true);
01250 TryPathReserve(v, true, true);
01251 for (; v->Next() != NULL; v = v->Next()) { }
01252 if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), true);
01253 }
01254
01255 st->MarkTilesDirty(false);
01256 st->UpdateVirtCoord();
01257 UpdateStationAcceptance(st, false);
01258 st->RecomputeIndustriesNear();
01259 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
01260 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
01261 SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
01262 }
01263
01264 return cost;
01265 }
01266
01267 static void MakeRailStationAreaSmaller(BaseStation *st)
01268 {
01269 TileArea ta = st->train_station;
01270
01271 restart:
01272
01273
01274 if (ta.w != 0 && ta.h != 0) {
01275
01276 for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(0, i));) {
01277
01278 if (++i == ta.h) {
01279 ta.tile += TileDiffXY(1, 0);
01280 ta.w--;
01281 goto restart;
01282 }
01283 }
01284
01285
01286 for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(ta.w - 1, i));) {
01287
01288 if (++i == ta.h) {
01289 ta.w--;
01290 goto restart;
01291 }
01292 }
01293
01294
01295 for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(i, 0));) {
01296
01297 if (++i == ta.w) {
01298 ta.tile += TileDiffXY(0, 1);
01299 ta.h--;
01300 goto restart;
01301 }
01302 }
01303
01304
01305 for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(i, ta.h - 1));) {
01306
01307 if (++i == ta.w) {
01308 ta.h--;
01309 goto restart;
01310 }
01311 }
01312 } else {
01313 ta.Clear();
01314 }
01315
01316 st->train_station = ta;
01317 }
01318
01329 template <class T>
01330 CommandCost RemoveFromRailBaseStation(TileArea ta, SmallVector<T *, 4> &affected_stations, DoCommandFlag flags, Money removal_cost, bool keep_rail)
01331 {
01332
01333 int quantity = 0;
01334 CommandCost total_cost(EXPENSES_CONSTRUCTION);
01335
01336
01337 TILE_AREA_LOOP(tile, ta) {
01338
01339 if (!HasStationTileRail(tile)) continue;
01340
01341
01342 CommandCost ret = EnsureNoVehicleOnGround(tile);
01343 if (ret.Failed()) continue;
01344
01345
01346 T *st = T::GetByTile(tile);
01347 if (st == NULL) continue;
01348
01349 if (_current_company != OWNER_WATER) {
01350 CommandCost ret = CheckOwnership(st->owner);
01351 if (ret.Failed()) continue;
01352 }
01353
01354
01355 quantity++;
01356
01357 if (keep_rail || IsStationTileBlocked(tile)) {
01358
01359
01360 total_cost.AddCost(-_price[PR_CLEAR_RAIL]);
01361 }
01362
01363 if (flags & DC_EXEC) {
01364
01365 uint specindex = GetCustomStationSpecIndex(tile);
01366 Track track = GetRailStationTrack(tile);
01367 Owner owner = GetTileOwner(tile);
01368 RailType rt = GetRailType(tile);
01369 Train *v = NULL;
01370
01371 if (HasStationReservation(tile)) {
01372 v = GetTrainForReservation(tile, track);
01373 if (v != NULL) {
01374
01375 FreeTrainTrackReservation(v);
01376 if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), false);
01377 Vehicle *temp = v;
01378 for (; temp->Next() != NULL; temp = temp->Next()) { }
01379 if (IsRailStationTile(temp->tile)) SetRailStationPlatformReservation(temp->tile, TrackdirToExitdir(ReverseTrackdir(temp->GetVehicleTrackdir())), false);
01380 }
01381 }
01382
01383 bool build_rail = keep_rail && !IsStationTileBlocked(tile);
01384
01385 DoClearSquare(tile);
01386 DeleteNewGRFInspectWindow(GSF_STATIONS, tile);
01387 if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt);
01388
01389 st->rect.AfterRemoveTile(st, tile);
01390 AddTrackToSignalBuffer(tile, track, owner);
01391 YapfNotifyTrackLayoutChange(tile, track);
01392
01393 DeallocateSpecFromStation(st, specindex);
01394
01395 affected_stations.Include(st);
01396
01397 if (v != NULL) {
01398
01399 if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true);
01400 TryPathReserve(v, true, true);
01401 for (; v->Next() != NULL; v = v->Next()) { }
01402 if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), true);
01403 }
01404 }
01405 }
01406
01407 if (quantity == 0) return_cmd_error(STR_ERROR_THERE_IS_NO_STATION);
01408
01409 for (T **stp = affected_stations.Begin(); stp != affected_stations.End(); stp++) {
01410 T *st = *stp;
01411
01412
01413
01414
01415 MakeRailStationAreaSmaller(st);
01416 UpdateStationSignCoord(st);
01417
01418
01419 if (st->train_station.tile == INVALID_TILE) {
01420 st->facilities &= ~FACIL_TRAIN;
01421 SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
01422 st->UpdateVirtCoord();
01423 DeleteStationIfEmpty(st);
01424 }
01425 }
01426
01427 total_cost.AddCost(quantity * removal_cost);
01428 return total_cost;
01429 }
01430
01442 CommandCost CmdRemoveFromRailStation(TileIndex start, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01443 {
01444 TileIndex end = p1 == 0 ? start : p1;
01445 if (start >= MapSize() || end >= MapSize()) return CMD_ERROR;
01446
01447 TileArea ta(start, end);
01448 SmallVector<Station *, 4> affected_stations;
01449
01450 CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_STATION_RAIL], HasBit(p2, 0));
01451 if (ret.Failed()) return ret;
01452
01453
01454 for (Station **stp = affected_stations.Begin(); stp != affected_stations.End(); stp++) {
01455 Station *st = *stp;
01456
01457 if (st->train_station.tile == INVALID_TILE) SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
01458 st->MarkTilesDirty(false);
01459 st->RecomputeIndustriesNear();
01460 }
01461
01462
01463 return ret;
01464 }
01465
01477 CommandCost CmdRemoveFromRailWaypoint(TileIndex start, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01478 {
01479 TileIndex end = p1 == 0 ? start : p1;
01480 if (start >= MapSize() || end >= MapSize()) return CMD_ERROR;
01481
01482 TileArea ta(start, end);
01483 SmallVector<Waypoint *, 4> affected_stations;
01484
01485 return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_WAYPOINT_RAIL], HasBit(p2, 0));
01486 }
01487
01488
01496 template <class T>
01497 CommandCost RemoveRailStation(T *st, DoCommandFlag flags)
01498 {
01499
01500 if (_current_company != OWNER_WATER) {
01501 CommandCost ret = CheckOwnership(st->owner);
01502 if (ret.Failed()) return ret;
01503 }
01504
01505
01506 TileArea ta = st->train_station;
01507
01508 assert(ta.w != 0 && ta.h != 0);
01509
01510 CommandCost cost(EXPENSES_CONSTRUCTION);
01511
01512 TILE_AREA_LOOP(tile, ta) {
01513
01514 if (!st->TileBelongsToRailStation(tile)) continue;
01515
01516 CommandCost ret = EnsureNoVehicleOnGround(tile);
01517 if (ret.Failed()) return ret;
01518
01519 cost.AddCost(_price[PR_CLEAR_STATION_RAIL]);
01520 if (flags & DC_EXEC) {
01521
01522 Track track = GetRailStationTrack(tile);
01523 Owner owner = GetTileOwner(tile);
01524 Train *v = NULL;
01525 if (HasStationReservation(tile)) {
01526 v = GetTrainForReservation(tile, track);
01527 if (v != NULL) FreeTrainTrackReservation(v);
01528 }
01529 DoClearSquare(tile);
01530 DeleteNewGRFInspectWindow(GSF_STATIONS, tile);
01531 AddTrackToSignalBuffer(tile, track, owner);
01532 YapfNotifyTrackLayoutChange(tile, track);
01533 if (v != NULL) TryPathReserve(v, true);
01534 }
01535 }
01536
01537 if (flags & DC_EXEC) {
01538 st->rect.AfterRemoveRect(st, st->train_station);
01539
01540 st->train_station.Clear();
01541
01542 st->facilities &= ~FACIL_TRAIN;
01543
01544 free(st->speclist);
01545 st->num_specs = 0;
01546 st->speclist = NULL;
01547 st->cached_anim_triggers = 0;
01548
01549 SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
01550 st->UpdateVirtCoord();
01551 DeleteStationIfEmpty(st);
01552 }
01553
01554 return cost;
01555 }
01556
01563 static CommandCost RemoveRailStation(TileIndex tile, DoCommandFlag flags)
01564 {
01565
01566 if (_current_company == OWNER_WATER) {
01567 return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAIL_STATION);
01568 }
01569
01570 Station *st = Station::GetByTile(tile);
01571 CommandCost cost = RemoveRailStation(st, flags);
01572
01573 if (flags & DC_EXEC) st->RecomputeIndustriesNear();
01574
01575 return cost;
01576 }
01577
01584 static CommandCost RemoveRailWaypoint(TileIndex tile, DoCommandFlag flags)
01585 {
01586
01587 if (_current_company == OWNER_WATER) {
01588 return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAIL_WAYPOINT);
01589 }
01590
01591 return RemoveRailStation(Waypoint::GetByTile(tile), flags);
01592 }
01593
01594
01600 static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
01601 {
01602 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
01603
01604 if (*primary_stop == NULL) {
01605
01606 return primary_stop;
01607 } else {
01608
01609 RoadStop *stop = *primary_stop;
01610 while (stop->next != NULL) stop = stop->next;
01611 return &stop->next;
01612 }
01613 }
01614
01615 static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags);
01616
01626 static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
01627 {
01628 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st);
01629 }
01630
01646 CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01647 {
01648 bool type = HasBit(p2, 0);
01649 bool is_drive_through = HasBit(p2, 1);
01650 RoadTypes rts = Extract<RoadTypes, 2, 2>(p2);
01651 StationID station_to_join = GB(p2, 16, 16);
01652 bool reuse = (station_to_join != NEW_STATION);
01653 if (!reuse) station_to_join = INVALID_STATION;
01654 bool distant_join = (station_to_join != INVALID_STATION);
01655
01656 uint8 width = (uint8)GB(p1, 0, 8);
01657 uint8 lenght = (uint8)GB(p1, 8, 8);
01658
01659
01660 if (width > _settings_game.station.station_spread || lenght > _settings_game.station.station_spread) return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT);
01661
01662 if (width == 0 || lenght == 0) return CMD_ERROR;
01663
01664 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, lenght - 1) == INVALID_TILE) return CMD_ERROR;
01665
01666 TileArea roadstop_area(tile, width, lenght);
01667
01668 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
01669
01670 if (!HasExactlyOneBit(rts) || !HasRoadTypesAvail(_current_company, rts)) return CMD_ERROR;
01671
01672
01673 if (!is_drive_through && HasBit(rts, ROADTYPE_TRAM)) return CMD_ERROR;
01674
01675 DiagDirection ddir = Extract<DiagDirection, 6, 2>(p2);
01676
01677
01678 if (!IsValidDiagDirection(ddir)) return CMD_ERROR;
01679
01680 if (is_drive_through && !IsValidAxis((Axis)ddir)) return CMD_ERROR;
01681
01682 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile, flags);
01683 if (ret.Failed()) return ret;
01684
01685
01686 CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * _price[type ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]);
01687 StationID est = INVALID_STATION;
01688 ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << ddir : 1 << ddir, is_drive_through, type, DiagDirToAxis(ddir), &est, rts);
01689 if (ret.Failed()) return ret;
01690 cost.AddCost(ret);
01691
01692 Station *st = NULL;
01693 ret = FindJoiningRoadStop(est, station_to_join, HasBit(p2, 5), roadstop_area, &st);
01694 if (ret.Failed()) return ret;
01695
01696
01697 if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
01698
01699
01700 if (!RoadStop::CanAllocateItem(roadstop_area.w * roadstop_area.h)) return_cmd_error(type ? STR_ERROR_TOO_MANY_TRUCK_STOPS : STR_ERROR_TOO_MANY_BUS_STOPS);
01701
01702 if (st != NULL) {
01703 if (st->owner != _current_company) {
01704 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
01705 }
01706
01707 CommandCost ret = st->rect.BeforeAddRect(roadstop_area.tile, roadstop_area.w, roadstop_area.h, StationRect::ADD_TEST);
01708 if (ret.Failed()) return ret;
01709 } else {
01710
01711 if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
01712
01713 if (flags & DC_EXEC) {
01714 st = new Station(tile);
01715
01716 st->town = ClosestTownFromTile(tile, UINT_MAX);
01717 st->string_id = GenerateStationName(st, tile, STATIONNAMING_ROAD);
01718
01719 if (Company::IsValidID(_current_company)) {
01720 SetBit(st->town->have_ratings, _current_company);
01721 }
01722 }
01723 }
01724
01725 if (flags & DC_EXEC) {
01726
01727 TILE_AREA_LOOP(cur_tile, roadstop_area) {
01728 RoadTypes cur_rts = GetRoadTypes(cur_tile);
01729 Owner road_owner = HasBit(cur_rts, ROADTYPE_ROAD) ? GetRoadOwner(cur_tile, ROADTYPE_ROAD) : _current_company;
01730 Owner tram_owner = HasBit(cur_rts, ROADTYPE_TRAM) ? GetRoadOwner(cur_tile, ROADTYPE_TRAM) : _current_company;
01731
01732 if (IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile)) {
01733 RemoveRoadStop(cur_tile, flags);
01734 }
01735
01736 RoadStop *road_stop = new RoadStop(cur_tile);
01737
01738 RoadStop **currstop = FindRoadStopSpot(type, st);
01739 *currstop = road_stop;
01740
01741 if (type) {
01742 st->truck_station.Add(cur_tile);
01743 } else {
01744 st->bus_station.Add(cur_tile);
01745 }
01746
01747
01748 st->AddFacility((type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP, cur_tile);
01749
01750 st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
01751
01752 RoadStopType rs_type = type ? ROADSTOP_TRUCK : ROADSTOP_BUS;
01753 if (is_drive_through) {
01754 MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, rs_type, rts | cur_rts, DiagDirToAxis(ddir));
01755 road_stop->MakeDriveThrough();
01756 } else {
01757 MakeRoadStop(cur_tile, st->owner, st->index, rs_type, rts, ddir);
01758 }
01759
01760 MarkTileDirtyByTile(cur_tile);
01761 }
01762 }
01763
01764 if (st != NULL) {
01765 st->UpdateVirtCoord();
01766 UpdateStationAcceptance(st, false);
01767 st->RecomputeIndustriesNear();
01768 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
01769 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
01770 SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_ROADVEHS);
01771 }
01772 return cost;
01773 }
01774
01775
01776 static Vehicle *ClearRoadStopStatusEnum(Vehicle *v, void *)
01777 {
01778 if (v->type == VEH_ROAD) {
01779
01780
01781
01782
01783
01784
01785 RoadVehicle *rv = RoadVehicle::From(v);
01786 if (HasBit(rv->state, RVS_IN_DT_ROAD_STOP)) rv->state &= RVSB_ROAD_STOP_TRACKDIR_MASK;
01787 }
01788
01789 return NULL;
01790 }
01791
01792
01799 static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
01800 {
01801 Station *st = Station::GetByTile(tile);
01802
01803 if (_current_company != OWNER_WATER) {
01804 CommandCost ret = CheckOwnership(st->owner);
01805 if (ret.Failed()) return ret;
01806 }
01807
01808 bool is_truck = IsTruckStop(tile);
01809
01810 RoadStop **primary_stop;
01811 RoadStop *cur_stop;
01812 if (is_truck) {
01813 primary_stop = &st->truck_stops;
01814 cur_stop = RoadStop::GetByTile(tile, ROADSTOP_TRUCK);
01815 } else {
01816 primary_stop = &st->bus_stops;
01817 cur_stop = RoadStop::GetByTile(tile, ROADSTOP_BUS);
01818 }
01819
01820 assert(cur_stop != NULL);
01821
01822
01823 if (IsDriveThroughStopTile(tile) && (flags & DC_BANKRUPT)) {
01824
01825 if (flags & DC_EXEC) FindVehicleOnPos(tile, NULL, &ClearRoadStopStatusEnum);
01826 } else {
01827 CommandCost ret = EnsureNoVehicleOnGround(tile);
01828 if (ret.Failed()) return ret;
01829 }
01830
01831 if (flags & DC_EXEC) {
01832 if (*primary_stop == cur_stop) {
01833
01834 *primary_stop = cur_stop->next;
01835
01836 if (*primary_stop == NULL) {
01837 st->facilities &= (is_truck ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP);
01838 }
01839 } else {
01840
01841 RoadStop *pred = *primary_stop;
01842 while (pred->next != cur_stop) pred = pred->next;
01843 pred->next = cur_stop->next;
01844 }
01845
01846 if (IsDriveThroughStopTile(tile)) {
01847
01848 cur_stop->ClearDriveThrough();
01849 } else {
01850 DoClearSquare(tile);
01851 }
01852
01853 SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_ROADVEHS);
01854 delete cur_stop;
01855
01856
01857 RoadVehicle *v;
01858 FOR_ALL_ROADVEHICLES(v) {
01859 if (v->First() == v && v->current_order.IsType(OT_GOTO_STATION) &&
01860 v->dest_tile == tile) {
01861 v->dest_tile = v->GetOrderStationLocation(st->index);
01862 }
01863 }
01864
01865 st->rect.AfterRemoveTile(st, tile);
01866
01867 st->UpdateVirtCoord();
01868 st->RecomputeIndustriesNear();
01869 DeleteStationIfEmpty(st);
01870
01871
01872 if (is_truck) {
01873 st->truck_station.Clear();
01874 for (const RoadStop *rs = st->truck_stops; rs != NULL; rs = rs->next) st->truck_station.Add(rs->xy);
01875 } else {
01876 st->bus_station.Clear();
01877 for (const RoadStop *rs = st->bus_stops; rs != NULL; rs = rs->next) st->bus_station.Add(rs->xy);
01878 }
01879 }
01880
01881 return CommandCost(EXPENSES_CONSTRUCTION, _price[is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS]);
01882 }
01883
01894 CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01895 {
01896 uint8 width = (uint8)GB(p1, 0, 8);
01897 uint8 height = (uint8)GB(p1, 8, 8);
01898
01899
01900 if (width == 0 || height == 0) return CMD_ERROR;
01901
01902 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
01903
01904 TileArea roadstop_area(tile, width, height);
01905
01906 int quantity = 0;
01907 CommandCost cost(EXPENSES_CONSTRUCTION);
01908 TILE_AREA_LOOP(cur_tile, roadstop_area) {
01909
01910 if (!IsTileType(cur_tile, MP_STATION) || !IsRoadStop(cur_tile) || (uint32)GetRoadStopType(cur_tile) != GB(p2, 0, 1)) continue;
01911
01912
01913 bool is_drive_through = IsDriveThroughStopTile(cur_tile);
01914 RoadTypes rts = GetRoadTypes(cur_tile);
01915 RoadBits road_bits = IsDriveThroughStopTile(cur_tile) ?
01916 ((GetRoadStopDir(cur_tile) == DIAGDIR_NE) ? ROAD_X : ROAD_Y) :
01917 DiagDirToRoadBits(GetRoadStopDir(cur_tile));
01918
01919 Owner road_owner = GetRoadOwner(cur_tile, ROADTYPE_ROAD);
01920 Owner tram_owner = GetRoadOwner(cur_tile, ROADTYPE_TRAM);
01921 CommandCost ret = RemoveRoadStop(cur_tile, flags);
01922 if (ret.Failed()) return ret;
01923 cost.AddCost(ret);
01924
01925 quantity++;
01926
01927 if ((flags & DC_EXEC) && is_drive_through) {
01928 MakeRoadNormal(cur_tile, road_bits, rts, ClosestTownFromTile(cur_tile, UINT_MAX)->index,
01929 road_owner, tram_owner);
01930 }
01931 }
01932
01933 if (quantity == 0) return_cmd_error(STR_ERROR_THERE_IS_NO_STATION);
01934
01935 return cost;
01936 }
01937
01945 static uint GetMinimalAirportDistanceToTile(const AirportSpec *as, TileIndex town_tile, TileIndex airport_tile)
01946 {
01947 uint ttx = TileX(town_tile);
01948 uint tty = TileY(town_tile);
01949
01950 uint atx = TileX(airport_tile);
01951 uint aty = TileY(airport_tile);
01952
01953 uint btx = TileX(airport_tile) + as->size_x - 1;
01954 uint bty = TileY(airport_tile) + as->size_y - 1;
01955
01956
01957
01958
01959 uint dx = ttx < atx ? atx - ttx : (ttx <= btx ? 0 : ttx - btx);
01960 uint dy = tty < aty ? aty - tty : (tty <= bty ? 0 : tty - bty);
01961
01962 return dx + dy;
01963 }
01964
01974 uint8 GetAirportNoiseLevelForTown(const AirportSpec *as, TileIndex town_tile, TileIndex tile)
01975 {
01976
01977
01978 if (as->noise_level < 2) return as->noise_level;
01979
01980 uint distance = GetMinimalAirportDistanceToTile(as, town_tile, tile);
01981
01982
01983
01984
01985
01986 uint8 town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
01987
01988
01989
01990 uint noise_reduction = distance / town_tolerance_distance;
01991
01992
01993
01994 return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
01995 }
01996
02004 Town *AirportGetNearestTown(const AirportSpec *as, TileIndex airport_tile)
02005 {
02006 Town *t, *nearest = NULL;
02007 uint add = as->size_x + as->size_y - 2;
02008 uint mindist = UINT_MAX - add;
02009 FOR_ALL_TOWNS(t) {
02010 if (DistanceManhattan(t->xy, airport_tile) < mindist + add) {
02011 uint dist = GetMinimalAirportDistanceToTile(as, t->xy, airport_tile);
02012 if (dist < mindist) {
02013 nearest = t;
02014 mindist = dist;
02015 }
02016 }
02017 }
02018
02019 return nearest;
02020 }
02021
02022
02024 void UpdateAirportsNoise()
02025 {
02026 Town *t;
02027 const Station *st;
02028
02029 FOR_ALL_TOWNS(t) t->noise_reached = 0;
02030
02031 FOR_ALL_STATIONS(st) {
02032 if (st->airport.tile != INVALID_TILE) {
02033 const AirportSpec *as = st->airport.GetSpec();
02034 Town *nearest = AirportGetNearestTown(as, st->airport.tile);
02035 nearest->noise_reached += GetAirportNoiseLevelForTown(as, nearest->xy, st->airport.tile);
02036 }
02037 }
02038 }
02039
02053 CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02054 {
02055 StationID station_to_join = GB(p2, 16, 16);
02056 bool reuse = (station_to_join != NEW_STATION);
02057 if (!reuse) station_to_join = INVALID_STATION;
02058 bool distant_join = (station_to_join != INVALID_STATION);
02059 byte airport_type = GB(p1, 0, 8);
02060 byte layout = GB(p1, 8, 8);
02061
02062 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
02063
02064 if (airport_type >= NUM_AIRPORTS) return CMD_ERROR;
02065
02066 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile, flags);
02067 if (ret.Failed()) return ret;
02068
02069
02070 const AirportSpec *as = AirportSpec::Get(airport_type);
02071 if (!as->IsAvailable() || layout >= as->num_table) return CMD_ERROR;
02072
02073 Direction rotation = as->rotation[layout];
02074 Town *t = ClosestTownFromTile(tile, UINT_MAX);
02075 int w = as->size_x;
02076 int h = as->size_y;
02077 if (rotation == DIR_E || rotation == DIR_W) Swap(w, h);
02078
02079 if (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread) {
02080 return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT);
02081 }
02082
02083 CommandCost cost = CheckFlatLand(TileArea(tile, w, h), flags);
02084 if (cost.Failed()) return cost;
02085
02086
02087 Town *nearest = AirportGetNearestTown(as, tile);
02088 uint newnoise_level = GetAirportNoiseLevelForTown(as, nearest->xy, tile);
02089
02090
02091 StringID authority_refuse_message = STR_NULL;
02092
02093 if (_settings_game.economy.station_noise_level) {
02094
02095 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
02096 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
02097 }
02098 } else {
02099 uint num = 0;
02100 const Station *st;
02101 FOR_ALL_STATIONS(st) {
02102 if (st->town == t && (st->facilities & FACIL_AIRPORT) && st->airport.type != AT_OILRIG) num++;
02103 }
02104 if (num >= 2) {
02105 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
02106 }
02107 }
02108
02109 if (authority_refuse_message != STR_NULL) {
02110 SetDParam(0, t->index);
02111 return_cmd_error(authority_refuse_message);
02112 }
02113
02114 Station *st = NULL;
02115 ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p2, 0), TileArea(tile, w, h), &st);
02116 if (ret.Failed()) return ret;
02117
02118
02119 if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join);
02120
02121
02122 if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
02123
02124 if (st != NULL) {
02125 if (st->owner != _current_company) {
02126 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
02127 }
02128
02129 CommandCost ret = st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TEST);
02130 if (ret.Failed()) return ret;
02131
02132 if (st->airport.tile != INVALID_TILE) {
02133 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
02134 }
02135 } else {
02136
02137 if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
02138
02139 if (flags & DC_EXEC) {
02140 st = new Station(tile);
02141
02142 st->town = t;
02143 st->string_id = GenerateStationName(st, tile, !(GetAirport(airport_type)->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_HELIPORT : STATIONNAMING_AIRPORT);
02144
02145 if (Company::IsValidID(_current_company)) {
02146 SetBit(st->town->have_ratings, _current_company);
02147 }
02148 }
02149 }
02150
02151 const AirportTileTable *it = as->table[layout];
02152 do {
02153 cost.AddCost(_price[PR_BUILD_STATION_AIRPORT]);
02154 } while ((++it)->ti.x != -0x80);
02155
02156 if (flags & DC_EXEC) {
02157
02158 nearest->noise_reached += newnoise_level;
02159
02160 st->AddFacility(FACIL_AIRPORT, tile);
02161 st->airport.type = airport_type;
02162 st->airport.layout = layout;
02163 st->airport.flags = 0;
02164 st->airport.rotation = rotation;
02165 st->airport.psa.ResetToZero();
02166
02167 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
02168
02169 it = as->table[layout];
02170 do {
02171 TileIndex cur_tile = tile + ToTileIndexDiff(it->ti);
02172 MakeAirport(cur_tile, st->owner, st->index, it->gfx, WATER_CLASS_INVALID);
02173 SetStationTileRandomBits(cur_tile, GB(Random(), 0, 4));
02174 st->airport.Add(cur_tile);
02175
02176 if (AirportTileSpec::Get(GetTranslatedAirportTileID(it->gfx))->animation.status != ANIM_STATUS_NO_ANIMATION) AddAnimatedTile(cur_tile);
02177 } while ((++it)->ti.x != -0x80);
02178
02179
02180 it = as->table[layout];
02181 do {
02182 TileIndex cur_tile = tile + ToTileIndexDiff(it->ti);
02183 AirportTileAnimationTrigger(st, cur_tile, AAT_BUILT);
02184 } while ((++it)->ti.x != -0x80);
02185
02186 UpdateAirplanesOnNewStation(st);
02187
02188 st->UpdateVirtCoord();
02189 UpdateStationAcceptance(st, false);
02190 st->RecomputeIndustriesNear();
02191 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
02192 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
02193 SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_PLANES);
02194
02195 if (_settings_game.economy.station_noise_level) {
02196 SetWindowDirty(WC_TOWN_VIEW, st->town->index);
02197 }
02198 }
02199
02200 return cost;
02201 }
02202
02209 static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags)
02210 {
02211 Station *st = Station::GetByTile(tile);
02212
02213 if (_current_company != OWNER_WATER) {
02214 CommandCost ret = CheckOwnership(st->owner);
02215 if (ret.Failed()) return ret;
02216 }
02217
02218 tile = st->airport.tile;
02219
02220 CommandCost cost(EXPENSES_CONSTRUCTION);
02221
02222 const Aircraft *a;
02223 FOR_ALL_AIRCRAFT(a) {
02224 if (!a->IsNormalAircraft()) continue;
02225 if (a->targetairport == st->index && a->state != FLYING) return CMD_ERROR;
02226 }
02227
02228 TILE_AREA_LOOP(tile_cur, st->airport) {
02229 if (!st->TileBelongsToAirport(tile_cur)) continue;
02230
02231 CommandCost ret = EnsureNoVehicleOnGround(tile_cur);
02232 if (ret.Failed()) return ret;
02233
02234 cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]);
02235
02236 if (flags & DC_EXEC) {
02237 if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur, false);
02238 DeleteAnimatedTile(tile_cur);
02239 DoClearSquare(tile_cur);
02240 DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur);
02241 }
02242 }
02243
02244 if (flags & DC_EXEC) {
02245 const AirportSpec *as = st->airport.GetSpec();
02246 for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
02247 DeleteWindowById(
02248 WC_VEHICLE_DEPOT, st->airport.GetHangarTile(i)
02249 );
02250 }
02251
02252
02253
02254
02255 Town *nearest = AirportGetNearestTown(as, tile);
02256 nearest->noise_reached -= GetAirportNoiseLevelForTown(as, nearest->xy, tile);
02257
02258 st->rect.AfterRemoveRect(st, st->airport);
02259
02260 st->airport.Clear();
02261 st->facilities &= ~FACIL_AIRPORT;
02262 st->airport.psa.ResetToZero();
02263
02264 SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_PLANES);
02265
02266 if (_settings_game.economy.station_noise_level) {
02267 SetWindowDirty(WC_TOWN_VIEW, st->town->index);
02268 }
02269
02270 st->UpdateVirtCoord();
02271 st->RecomputeIndustriesNear();
02272 DeleteStationIfEmpty(st);
02273 DeleteNewGRFInspectWindow(GSF_AIRPORTS, st->index);
02274 }
02275
02276 return cost;
02277 }
02278
02285 bool HasStationInUse(StationID station, bool include_company, CompanyID company)
02286 {
02287 const Vehicle *v;
02288 FOR_ALL_VEHICLES(v) {
02289 if ((v->owner == company) == include_company) {
02290 const Order *order;
02291 FOR_VEHICLE_ORDERS(v, order) {
02292 if ((order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT)) && order->GetDestination() == station) {
02293 return true;
02294 }
02295 }
02296 }
02297 }
02298 return false;
02299 }
02300
02301 static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
02302 {-1, 0},
02303 { 0, 0},
02304 { 0, 0},
02305 { 0, -1}
02306 };
02307 static const byte _dock_w_chk[4] = { 2, 1, 2, 1 };
02308 static const byte _dock_h_chk[4] = { 1, 2, 1, 2 };
02309
02319 CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02320 {
02321 StationID station_to_join = GB(p2, 16, 16);
02322 bool reuse = (station_to_join != NEW_STATION);
02323 if (!reuse) station_to_join = INVALID_STATION;
02324 bool distant_join = (station_to_join != INVALID_STATION);
02325
02326 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
02327
02328 DiagDirection direction = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
02329 if (direction == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
02330 direction = ReverseDiagDir(direction);
02331
02332
02333 if (HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
02334
02335 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile, flags);
02336 if (ret.Failed()) return ret;
02337
02338 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
02339
02340 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
02341 if (ret.Failed()) return ret;
02342
02343 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
02344
02345 if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
02346 return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
02347 }
02348
02349 if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
02350
02351
02352 WaterClass wc = GetWaterClass(tile_cur);
02353
02354 ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
02355 if (ret.Failed()) return ret;
02356
02357 tile_cur += TileOffsByDiagDir(direction);
02358 if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
02359 return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
02360 }
02361
02362
02363 Station *st = NULL;
02364 ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p1, 0),
02365 TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02366 _dock_w_chk[direction], _dock_h_chk[direction]), &st);
02367 if (ret.Failed()) return ret;
02368
02369
02370 if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join);
02371
02372
02373 if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
02374
02375 if (st != NULL) {
02376 if (st->owner != _current_company) {
02377 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
02378 }
02379
02380 CommandCost ret = st->rect.BeforeAddRect(
02381 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02382 _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TEST);
02383 if (ret.Failed()) return ret;
02384
02385 if (st->dock_tile != INVALID_TILE) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_DOCK);
02386 } else {
02387
02388 if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
02389
02390 if (flags & DC_EXEC) {
02391 st = new Station(tile);
02392
02393 st->town = ClosestTownFromTile(tile, UINT_MAX);
02394 st->string_id = GenerateStationName(st, tile, STATIONNAMING_DOCK);
02395
02396 if (Company::IsValidID(_current_company)) {
02397 SetBit(st->town->have_ratings, _current_company);
02398 }
02399 }
02400 }
02401
02402 if (flags & DC_EXEC) {
02403 st->dock_tile = tile;
02404 st->AddFacility(FACIL_DOCK, tile);
02405
02406 st->rect.BeforeAddRect(
02407 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02408 _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TRY);
02409
02410 MakeDock(tile, st->owner, st->index, direction, wc);
02411
02412 st->UpdateVirtCoord();
02413 UpdateStationAcceptance(st, false);
02414 st->RecomputeIndustriesNear();
02415 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
02416 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
02417 SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_SHIPS);
02418 }
02419
02420 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
02421 }
02422
02429 static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags)
02430 {
02431 Station *st = Station::GetByTile(tile);
02432 CommandCost ret = CheckOwnership(st->owner);
02433 if (ret.Failed()) return ret;
02434
02435 TileIndex tile1 = st->dock_tile;
02436 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
02437
02438 ret = EnsureNoVehicleOnGround(tile1);
02439 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
02440 if (ret.Failed()) return ret;
02441
02442 if (flags & DC_EXEC) {
02443 DoClearSquare(tile1);
02444 MarkTileDirtyByTile(tile1);
02445 MakeWaterKeepingClass(tile2, st->owner);
02446
02447 st->rect.AfterRemoveTile(st, tile1);
02448 st->rect.AfterRemoveTile(st, tile2);
02449
02450 st->dock_tile = INVALID_TILE;
02451 st->facilities &= ~FACIL_DOCK;
02452
02453 SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_SHIPS);
02454 st->UpdateVirtCoord();
02455 st->RecomputeIndustriesNear();
02456 DeleteStationIfEmpty(st);
02457 }
02458
02459 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]);
02460 }
02461
02462 #include "table/station_land.h"
02463
02464 const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx)
02465 {
02466 return &_station_display_datas[st][gfx];
02467 }
02468
02469 static void DrawTile_Station(TileInfo *ti)
02470 {
02471 const DrawTileSprites *t = NULL;
02472 RoadTypes roadtypes;
02473 int32 total_offset;
02474 int32 custom_ground_offset;
02475 const RailtypeInfo *rti = NULL;
02476 uint32 relocation = 0;
02477 const BaseStation *st = NULL;
02478 const StationSpec *statspec = NULL;
02479
02480 if (HasStationRail(ti->tile)) {
02481 rti = GetRailTypeInfo(GetRailType(ti->tile));
02482 roadtypes = ROADTYPES_NONE;
02483 total_offset = rti->GetRailtypeSpriteOffset();
02484 custom_ground_offset = rti->fallback_railtype;
02485
02486 if (IsCustomStationSpecIndex(ti->tile)) {
02487
02488 st = BaseStation::GetByTile(ti->tile);
02489 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
02490
02491 if (statspec != NULL) {
02492 uint tile = GetStationGfx(ti->tile);
02493
02494 relocation = GetCustomStationRelocation(statspec, st, ti->tile);
02495
02496 if (HasBit(statspec->callback_mask, CBM_STATION_SPRITE_LAYOUT)) {
02497 uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile);
02498 if (callback != CALLBACK_FAILED) tile = (callback & ~1) + GetRailStationAxis(ti->tile);
02499 }
02500
02501
02502 if (statspec->renderdata != NULL) {
02503 t = &statspec->renderdata[tile < statspec->tiles ? tile : (uint)GetRailStationAxis(ti->tile)];
02504 }
02505 }
02506 }
02507 } else {
02508 roadtypes = IsRoadStop(ti->tile) ? GetRoadTypes(ti->tile) : ROADTYPES_NONE;
02509 total_offset = 0;
02510 custom_ground_offset = 0;
02511 }
02512
02513 if (IsAirport(ti->tile)) {
02514 StationGfx gfx = GetAirportGfx(ti->tile);
02515 if (gfx >= NEW_AIRPORTTILE_OFFSET) {
02516 const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
02517 if (ats->grf_prop.spritegroup[0] != NULL && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), gfx, ats)) {
02518 return;
02519 }
02520
02521
02522 assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
02523 gfx = ats->grf_prop.subst_id;
02524 }
02525 switch (gfx) {
02526 case APT_RADAR_GRASS_FENCE_SW:
02527 t = &_station_display_datas_airport_radar_grass_fence_sw[GetAnimationFrame(ti->tile)];
02528 break;
02529 case APT_GRASS_FENCE_NE_FLAG:
02530 t = &_station_display_datas_airport_flag_grass_fence_ne[GetAnimationFrame(ti->tile)];
02531 break;
02532 case APT_RADAR_FENCE_SW:
02533 t = &_station_display_datas_airport_radar_fence_sw[GetAnimationFrame(ti->tile)];
02534 break;
02535 case APT_RADAR_FENCE_NE:
02536 t = &_station_display_datas_airport_radar_fence_ne[GetAnimationFrame(ti->tile)];
02537 break;
02538 case APT_GRASS_FENCE_NE_FLAG_2:
02539 t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetAnimationFrame(ti->tile)];
02540 break;
02541 }
02542 }
02543
02544 Owner owner = GetTileOwner(ti->tile);
02545
02546 PaletteID palette;
02547 if (Company::IsValidID(owner)) {
02548 palette = COMPANY_SPRITE_COLOUR(owner);
02549 } else {
02550
02551 palette = PALETTE_TO_GREY;
02552 }
02553
02554 if (t == NULL || t->seq == NULL) t = GetStationTileLayout(GetStationType(ti->tile), GetStationGfx(ti->tile));
02555
02556
02557 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
02558 if (statspec != NULL && HasBit(statspec->flags, SSF_CUSTOM_FOUNDATIONS)) {
02559
02560 SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile);
02561
02562 if (HasBit(statspec->flags, SSF_EXTENDED_FOUNDATIONS)) {
02563
02564
02565 static const uint8 foundation_parts[] = {
02566 0, 0, 0, 0,
02567 0, 1, 2, 3,
02568 0, 4, 5, 6,
02569 7, 8, 9
02570 };
02571
02572 AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
02573 } else {
02574
02575
02576
02577
02578 static const uint8 composite_foundation_parts[] = {
02579
02580 0x00, 0xD1, 0xE4, 0xE0,
02581
02582 0xCA, 0xC9, 0xC4, 0xC0,
02583
02584 0xD2, 0x91, 0xE4, 0xA0,
02585
02586 0x4A, 0x09, 0x44
02587 };
02588
02589 uint8 parts = composite_foundation_parts[ti->tileh];
02590
02591
02592
02593 uint z;
02594 Slope slope = GetFoundationSlope(ti->tile, &z);
02595 if (!HasFoundationNW(ti->tile, slope, z)) ClrBit(parts, 6);
02596 if (!HasFoundationNE(ti->tile, slope, z)) ClrBit(parts, 7);
02597
02598 if (parts == 0) {
02599
02600
02601
02602 goto draw_default_foundation;
02603 }
02604
02605 StartSpriteCombine();
02606 for (int i = 0; i < 8; i++) {
02607 if (HasBit(parts, i)) {
02608 AddSortableSpriteToDraw(image + i, PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
02609 }
02610 }
02611 EndSpriteCombine();
02612 }
02613
02614 OffsetGroundSprite(31, 1);
02615 ti->z += ApplyFoundationToSlope(FOUNDATION_LEVELED, &ti->tileh);
02616 } else {
02617 draw_default_foundation:
02618 DrawFoundation(ti, FOUNDATION_LEVELED);
02619 }
02620 }
02621
02622 if (IsBuoy(ti->tile)) {
02623 DrawWaterClassGround(ti);
02624 SpriteID sprite = GetCanalSprite(CF_BUOY, ti->tile);
02625 if (sprite != 0) total_offset = sprite - SPR_IMG_BUOY;
02626 } else if (IsDock(ti->tile) || (IsOilRig(ti->tile) && IsTileOnWater(ti->tile))) {
02627 if (ti->tileh == SLOPE_FLAT) {
02628 DrawWaterClassGround(ti);
02629 } else {
02630 assert(IsDock(ti->tile));
02631 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
02632 WaterClass wc = GetWaterClass(water_tile);
02633 if (wc == WATER_CLASS_SEA) {
02634 DrawShoreTile(ti->tileh);
02635 } else {
02636 DrawClearLandTile(ti, 3);
02637 }
02638 }
02639 } else {
02640 SpriteID image = t->ground.sprite;
02641 PaletteID pal = t->ground.pal;
02642 if (rti != NULL && rti->UsesOverlay() && (image == SPR_RAIL_TRACK_X || image == SPR_RAIL_TRACK_Y)) {
02643 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
02644 DrawGroundSprite(SPR_FLAT_GRASS_TILE, PAL_NONE);
02645 DrawGroundSprite(ground + (image == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PAL_NONE);
02646
02647 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
02648 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
02649 DrawGroundSprite(overlay + (image == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PALETTE_CRASH);
02650 }
02651 } else {
02652 if (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) {
02653 image += GetCustomStationGroundRelocation(statspec, st, ti->tile);
02654 image += custom_ground_offset;
02655 } else {
02656 image += total_offset;
02657 }
02658 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
02659
02660
02661 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
02662 const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
02663 DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
02664 }
02665 }
02666 }
02667
02668 if (HasStationRail(ti->tile) && HasCatenaryDrawn(GetRailType(ti->tile)) && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
02669
02670 if (HasBit(roadtypes, ROADTYPE_TRAM)) {
02671 Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
02672 DrawGroundSprite((HasBit(roadtypes, ROADTYPE_ROAD) ? SPR_TRAMWAY_OVERLAY : SPR_TRAMWAY_TRAM) + (axis ^ 1), PAL_NONE);
02673 DrawTramCatenary(ti, axis == AXIS_X ? ROAD_X : ROAD_Y);
02674 }
02675
02676 if (IsRailWaypoint(ti->tile)) {
02677
02678 total_offset = 0;
02679 }
02680
02681 DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
02682 }
02683
02684 void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
02685 {
02686 int32 total_offset = 0;
02687 PaletteID pal = COMPANY_SPRITE_COLOUR(_local_company);
02688 const DrawTileSprites *t = GetStationTileLayout(st, image);
02689 const RailtypeInfo *rti = NULL;
02690
02691 if (railtype != INVALID_RAILTYPE) {
02692 rti = GetRailTypeInfo(railtype);
02693 total_offset = rti->GetRailtypeSpriteOffset();
02694 }
02695
02696 SpriteID img = t->ground.sprite;
02697 if ((img == SPR_RAIL_TRACK_X || img == SPR_RAIL_TRACK_Y) && rti->UsesOverlay()) {
02698 SpriteID ground = GetCustomRailSprite(rti, INVALID_TILE, RTSG_GROUND);
02699 DrawSprite(SPR_FLAT_GRASS_TILE, PAL_NONE, x, y);
02700 DrawSprite(ground + (img == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PAL_NONE, x, y);
02701 } else {
02702 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
02703 }
02704
02705 if (roadtype == ROADTYPE_TRAM) {
02706 DrawSprite(SPR_TRAMWAY_TRAM + (t->ground.sprite == SPR_ROAD_PAVED_STRAIGHT_X ? 1 : 0), PAL_NONE, x, y);
02707 }
02708
02709
02710 DrawRailTileSeqInGUI(x, y, t, st == STATION_WAYPOINT ? 0 : total_offset, 0, pal);
02711 }
02712
02713 static uint GetSlopeZ_Station(TileIndex tile, uint x, uint y)
02714 {
02715 return GetTileMaxZ(tile);
02716 }
02717
02718 static Foundation GetFoundation_Station(TileIndex tile, Slope tileh)
02719 {
02720 return FlatteningFoundation(tileh);
02721 }
02722
02723 static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
02724 {
02725 td->owner[0] = GetTileOwner(tile);
02726 if (IsDriveThroughStopTile(tile)) {
02727 Owner road_owner = INVALID_OWNER;
02728 Owner tram_owner = INVALID_OWNER;
02729 RoadTypes rts = GetRoadTypes(tile);
02730 if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
02731 if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
02732
02733
02734 if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) ||
02735 (road_owner != INVALID_OWNER && road_owner != td->owner[0])) {
02736 uint i = 1;
02737 if (road_owner != INVALID_OWNER) {
02738 td->owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
02739 td->owner[i] = road_owner;
02740 i++;
02741 }
02742 if (tram_owner != INVALID_OWNER) {
02743 td->owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
02744 td->owner[i] = tram_owner;
02745 }
02746 }
02747 }
02748 td->build_date = BaseStation::GetByTile(tile)->build_date;
02749
02750 if (HasStationTileRail(tile)) {
02751 const StationSpec *spec = GetStationSpec(tile);
02752
02753 if (spec != NULL) {
02754 td->station_class = StationClass::GetName(spec->cls_id);
02755 td->station_name = spec->name;
02756
02757 if (spec->grf_prop.grffile != NULL) {
02758 const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grffile->grfid);
02759 td->grf = gc->GetName();
02760 }
02761 }
02762
02763 const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
02764 td->rail_speed = rti->max_speed;
02765 }
02766
02767 if (IsAirport(tile)) {
02768 const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
02769 td->airport_class = AirportClass::GetName(as->cls_id);
02770 td->airport_name = as->name;
02771
02772 const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile);
02773 td->airport_tile_name = ats->name;
02774
02775 if (as->grf_prop.grffile != NULL) {
02776 const GRFConfig *gc = GetGRFConfig(as->grf_prop.grffile->grfid);
02777 td->grf = gc->GetName();
02778 } else if (ats->grf_prop.grffile != NULL) {
02779 const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grffile->grfid);
02780 td->grf = gc->GetName();
02781 }
02782 }
02783
02784 StringID str;
02785 switch (GetStationType(tile)) {
02786 default: NOT_REACHED();
02787 case STATION_RAIL: str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
02788 case STATION_AIRPORT:
02789 str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
02790 break;
02791 case STATION_TRUCK: str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
02792 case STATION_BUS: str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
02793 case STATION_OILRIG: str = STR_INDUSTRY_NAME_OIL_RIG; break;
02794 case STATION_DOCK: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
02795 case STATION_BUOY: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
02796 case STATION_WAYPOINT: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
02797 }
02798 td->str = str;
02799 }
02800
02801
02802 static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
02803 {
02804 TrackBits trackbits = TRACK_BIT_NONE;
02805
02806 switch (mode) {
02807 case TRANSPORT_RAIL:
02808 if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
02809 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
02810 }
02811 break;
02812
02813 case TRANSPORT_WATER:
02814
02815 if (IsBuoy(tile)) {
02816 trackbits = TRACK_BIT_ALL;
02817
02818 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
02819
02820 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
02821 }
02822 break;
02823
02824 case TRANSPORT_ROAD:
02825 if ((GetRoadTypes(tile) & sub_mode) != 0 && IsRoadStop(tile)) {
02826 DiagDirection dir = GetRoadStopDir(tile);
02827 Axis axis = DiagDirToAxis(dir);
02828
02829 if (side != INVALID_DIAGDIR) {
02830 if (axis != DiagDirToAxis(side) || (IsStandardRoadStopTile(tile) && dir != side)) break;
02831 }
02832
02833 trackbits = AxisToTrackBits(axis);
02834 }
02835 break;
02836
02837 default:
02838 break;
02839 }
02840
02841 return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), TRACKDIR_BIT_NONE);
02842 }
02843
02844
02845 static void TileLoop_Station(TileIndex tile)
02846 {
02847
02848
02849 switch (GetStationType(tile)) {
02850 case STATION_AIRPORT:
02851 AirportTileAnimationTrigger(Station::GetByTile(tile), tile, AAT_TILELOOP);
02852 break;
02853
02854 case STATION_DOCK:
02855 if (GetTileSlope(tile, NULL) != SLOPE_FLAT) break;
02856
02857 case STATION_OILRIG:
02858 case STATION_BUOY:
02859 TileLoop_Water(tile);
02860 break;
02861
02862 default: break;
02863 }
02864 }
02865
02866
02867 static void AnimateTile_Station(TileIndex tile)
02868 {
02869 if (HasStationRail(tile)) {
02870 AnimateStationTile(tile);
02871 return;
02872 }
02873
02874 if (IsAirport(tile)) {
02875 AnimateAirportTile(tile);
02876 }
02877 }
02878
02879
02880 static bool ClickTile_Station(TileIndex tile)
02881 {
02882 const BaseStation *bst = BaseStation::GetByTile(tile);
02883
02884 if (bst->facilities & FACIL_WAYPOINT) {
02885 ShowWaypointWindow(Waypoint::From(bst));
02886 } else if (IsHangar(tile)) {
02887 const Station *st = Station::From(bst);
02888 ShowDepotWindow(st->airport.GetHangarTile(st->airport.GetHangarNum(tile)), VEH_AIRCRAFT);
02889 } else {
02890 ShowStationViewWindow(bst->index);
02891 }
02892 return true;
02893 }
02894
02895 static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
02896 {
02897 if (v->type == VEH_TRAIN) {
02898 StationID station_id = GetStationIndex(tile);
02899 if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
02900 if (!IsRailStation(tile) || !v->IsFrontEngine()) return VETSB_CONTINUE;
02901
02902 int station_ahead;
02903 int station_length;
02904 int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
02905
02906
02907
02908
02909
02910 if (!IsInsideBS(stop + station_ahead, station_length, TILE_SIZE)) return VETSB_CONTINUE;
02911
02912 DiagDirection dir = DirToDiagDir(v->direction);
02913
02914 x &= 0xF;
02915 y &= 0xF;
02916
02917 if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
02918 if (y == TILE_SIZE / 2) {
02919 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
02920 stop &= TILE_SIZE - 1;
02921
02922 if (x == stop) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET);
02923 if (x < stop) {
02924 uint16 spd;
02925
02926 v->vehstatus |= VS_TRAIN_SLOWING;
02927 spd = max(0, (stop - x) * 20 - 15);
02928 if (spd < v->cur_speed) v->cur_speed = spd;
02929 }
02930 }
02931 } else if (v->type == VEH_ROAD) {
02932 RoadVehicle *rv = RoadVehicle::From(v);
02933 if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
02934 if (IsRoadStop(tile) && rv->IsFrontEngine()) {
02935
02936 return RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv) ? VETSB_CONTINUE : VETSB_CANNOT_ENTER;
02937 }
02938 }
02939 }
02940
02941 return VETSB_CONTINUE;
02942 }
02943
02950 static bool StationHandleBigTick(BaseStation *st)
02951 {
02952 if (!st->IsInUse()) {
02953 if (++st->delete_ctr >= 8) delete st;
02954 return false;
02955 }
02956
02957 if ((st->facilities & FACIL_WAYPOINT) == 0) UpdateStationAcceptance(Station::From(st), true);
02958
02959 return true;
02960 }
02961
02962 static inline void byte_inc_sat(byte *p)
02963 {
02964 byte b = *p + 1;
02965 if (b != 0) *p = b;
02966 }
02967
02968 static void UpdateStationRating(Station *st)
02969 {
02970 bool waiting_changed = false;
02971
02972 byte_inc_sat(&st->time_since_load);
02973 byte_inc_sat(&st->time_since_unload);
02974
02975 const CargoSpec *cs;
02976 FOR_ALL_CARGOSPECS(cs) {
02977 GoodsEntry *ge = &st->goods[cs->Index()];
02978
02979
02980
02981 if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP) && ge->rating < INITIAL_STATION_RATING) {
02982 ge->rating++;
02983 }
02984
02985
02986 if (HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) {
02987 byte_inc_sat(&ge->days_since_pickup);
02988
02989 bool skip = false;
02990 int rating = 0;
02991 uint waiting = ge->cargo.Count();
02992
02993 if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) {
02994
02995
02996
02997
02998 uint last_speed = ge->last_speed;
02999 if (last_speed == 0) last_speed = 0xFF;
03000
03001 uint32 var18 = min(ge->days_since_pickup, 0xFF) | (min(waiting, 0xFFFF) << 8) | (min(last_speed, 0xFF) << 24);
03002
03003 uint32 var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
03004 uint16 callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
03005 if (callback != CALLBACK_FAILED) {
03006 skip = true;
03007 rating = GB(callback, 0, 14);
03008
03009
03010 if (HasBit(callback, 14)) rating -= 0x4000;
03011 }
03012 }
03013
03014 if (!skip) {
03015 int b = ge->last_speed - 85;
03016 if (b >= 0) rating += b >> 2;
03017
03018 byte days = ge->days_since_pickup;
03019 if (st->last_vehicle_type == VEH_SHIP) days >>= 2;
03020 (days > 21) ||
03021 (rating += 25, days > 12) ||
03022 (rating += 25, days > 6) ||
03023 (rating += 45, days > 3) ||
03024 (rating += 35, true);
03025
03026 (rating -= 90, waiting > 1500) ||
03027 (rating += 55, waiting > 1000) ||
03028 (rating += 35, waiting > 600) ||
03029 (rating += 10, waiting > 300) ||
03030 (rating += 20, waiting > 100) ||
03031 (rating += 10, true);
03032 }
03033
03034 if (Company::IsValidID(st->owner) && HasBit(st->town->statues, st->owner)) rating += 26;
03035
03036 byte age = ge->last_age;
03037 (age >= 3) ||
03038 (rating += 10, age >= 2) ||
03039 (rating += 10, age >= 1) ||
03040 (rating += 13, true);
03041
03042 {
03043 int or_ = ge->rating;
03044
03045
03046 ge->rating = rating = or_ + Clamp(Clamp(rating, 0, 255) - or_, -2, 2);
03047
03048
03049
03050 if (rating <= 64 && waiting >= 200) {
03051 int dec = Random() & 0x1F;
03052 if (waiting < 400) dec &= 7;
03053 waiting -= dec + 1;
03054 waiting_changed = true;
03055 }
03056
03057
03058 if (rating <= 127 && waiting != 0) {
03059 uint32 r = Random();
03060 if (rating <= (int)GB(r, 0, 7)) {
03061
03062 waiting = max((int)waiting - (int)GB(r, 8, 2) - 1, 0);
03063 waiting_changed = true;
03064 }
03065 }
03066
03067
03068
03069
03070 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
03071 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
03072 static const uint MAX_WAITING_CARGO = 1 << 15;
03073
03074 if (waiting > WAITING_CARGO_THRESHOLD) {
03075 uint difference = waiting - WAITING_CARGO_THRESHOLD;
03076 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
03077
03078 waiting = min(waiting, MAX_WAITING_CARGO);
03079 waiting_changed = true;
03080 }
03081
03082 if (waiting_changed) ge->cargo.Truncate(waiting);
03083 }
03084 }
03085 }
03086
03087 StationID index = st->index;
03088 if (waiting_changed) {
03089 SetWindowDirty(WC_STATION_VIEW, index);
03090 } else {
03091 SetWindowWidgetDirty(WC_STATION_VIEW, index, SVW_RATINGLIST);
03092 }
03093 }
03094
03095
03096 static void StationHandleSmallTick(BaseStation *st)
03097 {
03098 if ((st->facilities & FACIL_WAYPOINT) != 0 || !st->IsInUse()) return;
03099
03100 byte b = st->delete_ctr + 1;
03101 if (b >= 185) b = 0;
03102 st->delete_ctr = b;
03103
03104 if (b == 0) UpdateStationRating(Station::From(st));
03105 }
03106
03107 void OnTick_Station()
03108 {
03109 if (_game_mode == GM_EDITOR) return;
03110
03111 BaseStation *st;
03112 FOR_ALL_BASE_STATIONS(st) {
03113 StationHandleSmallTick(st);
03114
03115
03116
03117
03118 if ((_tick_counter + st->index) % 250 == 0) {
03119
03120 if (!StationHandleBigTick(st)) continue;
03121 TriggerStationAnimation(st, st->xy, SAT_250_TICKS);
03122 if (Station::IsExpected(st)) AirportAnimationTrigger(Station::From(st), AAT_STATION_250_TICKS);
03123 }
03124 }
03125 }
03126
03127 void StationMonthlyLoop()
03128 {
03129
03130 }
03131
03132
03133 void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
03134 {
03135 Station *st;
03136
03137 FOR_ALL_STATIONS(st) {
03138 if (st->owner == owner &&
03139 DistanceManhattan(tile, st->xy) <= radius) {
03140 for (CargoID i = 0; i < NUM_CARGO; i++) {
03141 GoodsEntry *ge = &st->goods[i];
03142
03143 if (ge->acceptance_pickup != 0) {
03144 ge->rating = Clamp(ge->rating + amount, 0, 255);
03145 }
03146 }
03147 }
03148 }
03149 }
03150
03151 static uint UpdateStationWaiting(Station *st, CargoID type, uint amount, SourceType source_type, SourceID source_id)
03152 {
03153
03154
03155 if (!CargoPacket::CanAllocateItem()) return 0;
03156
03157 GoodsEntry &ge = st->goods[type];
03158 amount += ge.amount_fract;
03159 ge.amount_fract = GB(amount, 0, 8);
03160
03161 amount >>= 8;
03162
03163 if (amount == 0) return 0;
03164
03165 ge.cargo.Append(new CargoPacket(st->index, st->xy, amount, source_type, source_id));
03166
03167 if (!HasBit(ge.acceptance_pickup, GoodsEntry::PICKUP)) {
03168 InvalidateWindowData(WC_STATION_LIST, st->index);
03169 SetBit(ge.acceptance_pickup, GoodsEntry::PICKUP);
03170 }
03171
03172 TriggerStationAnimation(st, st->xy, SAT_NEW_CARGO, type);
03173 AirportAnimationTrigger(st, AAT_STATION_NEW_CARGO, type);
03174
03175 SetWindowDirty(WC_STATION_VIEW, st->index);
03176 st->MarkTilesDirty(true);
03177 return amount;
03178 }
03179
03180 static bool IsUniqueStationName(const char *name)
03181 {
03182 const Station *st;
03183
03184 FOR_ALL_STATIONS(st) {
03185 if (st->name != NULL && strcmp(st->name, name) == 0) return false;
03186 }
03187
03188 return true;
03189 }
03190
03200 CommandCost CmdRenameStation(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
03201 {
03202 Station *st = Station::GetIfValid(p1);
03203 if (st == NULL) return CMD_ERROR;
03204
03205 CommandCost ret = CheckOwnership(st->owner);
03206 if (ret.Failed()) return ret;
03207
03208 bool reset = StrEmpty(text);
03209
03210 if (!reset) {
03211 if (Utf8StringLength(text) >= MAX_LENGTH_STATION_NAME_CHARS) return CMD_ERROR;
03212 if (!IsUniqueStationName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
03213 }
03214
03215 if (flags & DC_EXEC) {
03216 free(st->name);
03217 st->name = reset ? NULL : strdup(text);
03218
03219 st->UpdateVirtCoord();
03220 InvalidateWindowData(WC_STATION_LIST, st->owner, 1);
03221 }
03222
03223 return CommandCost();
03224 }
03225
03232 void FindStationsAroundTiles(const TileArea &location, StationList *stations)
03233 {
03234
03235 int max_rad = (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED);
03236
03237 for (int dy = -max_rad; dy < location.h + max_rad; dy++) {
03238 for (int dx = -max_rad; dx < location.w + max_rad; dx++) {
03239 TileIndex cur_tile = TileAddWrap(location.tile, dx, dy);
03240 if (cur_tile == INVALID_TILE || !IsTileType(cur_tile, MP_STATION)) continue;
03241
03242 Station *st = Station::GetByTile(cur_tile);
03243 if (st == NULL) continue;
03244
03245 if (_settings_game.station.modified_catchment) {
03246 int rad = st->GetCatchmentRadius();
03247 if (dx < -rad || dx >= rad + location.w || dy < -rad || dy >= rad + location.h) continue;
03248 }
03249
03250
03251
03252
03253 stations->Include(st);
03254 }
03255 }
03256 }
03257
03262 const StationList *StationFinder::GetStations()
03263 {
03264 if (this->tile != INVALID_TILE) {
03265 FindStationsAroundTiles(*this, &this->stations);
03266 this->tile = INVALID_TILE;
03267 }
03268 return &this->stations;
03269 }
03270
03271 uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList *all_stations)
03272 {
03273
03274 if (amount == 0) return 0;
03275
03276 Station *st1 = NULL;
03277 Station *st2 = NULL;
03278 uint best_rating1 = 0;
03279 uint best_rating2 = 0;
03280
03281 for (Station * const *st_iter = all_stations->Begin(); st_iter != all_stations->End(); ++st_iter) {
03282 Station *st = *st_iter;
03283
03284
03285 if (st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) continue;
03286
03287 if (st->goods[type].rating == 0) continue;
03288
03289 if (_settings_game.order.selectgoods && st->goods[type].last_speed == 0) continue;
03290
03291 if (IsCargoInClass(type, CC_PASSENGERS)) {
03292 if (st->facilities == FACIL_TRUCK_STOP) continue;
03293 } else {
03294 if (st->facilities == FACIL_BUS_STOP) continue;
03295 }
03296
03297
03298 if (st1 == NULL || st->goods[type].rating >= best_rating1) {
03299 st2 = st1; best_rating2 = best_rating1; st1 = st; best_rating1 = st->goods[type].rating;
03300 } else if (st2 == NULL || st->goods[type].rating >= best_rating2) {
03301 st2 = st; best_rating2 = st->goods[type].rating;
03302 }
03303 }
03304
03305
03306 if (st1 == NULL) return 0;
03307
03308
03309
03310 amount *= best_rating1 + 1;
03311
03312 if (st2 == NULL) {
03313
03314 return UpdateStationWaiting(st1, type, amount, source_type, source_id);
03315 }
03316
03317
03318 assert(st1 != NULL);
03319 assert(st2 != NULL);
03320 assert(best_rating1 != 0 || best_rating2 != 0);
03321
03322
03323
03324
03325
03326
03327 uint worst_cargo = amount * best_rating2 / (best_rating1 + best_rating2);
03328 assert(worst_cargo <= (amount - worst_cargo));
03329
03330
03331 uint moved = UpdateStationWaiting(st1, type, amount - worst_cargo, source_type, source_id);
03332
03333
03334 return moved + UpdateStationWaiting(st2, type, worst_cargo, source_type, source_id);
03335 }
03336
03337 void BuildOilRig(TileIndex tile)
03338 {
03339 if (!Station::CanAllocateItem()) {
03340 DEBUG(misc, 0, "Can't allocate station for oilrig at 0x%X, reverting to oilrig only", tile);
03341 return;
03342 }
03343
03344 Station *st = new Station(tile);
03345 st->town = ClosestTownFromTile(tile, UINT_MAX);
03346
03347 st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
03348
03349 assert(IsTileType(tile, MP_INDUSTRY));
03350 DeleteAnimatedTile(tile);
03351 MakeOilrig(tile, st->index, GetWaterClass(tile));
03352
03353 st->owner = OWNER_NONE;
03354 st->airport.type = AT_OILRIG;
03355 st->airport.Add(tile);
03356 st->dock_tile = tile;
03357 st->facilities = FACIL_AIRPORT | FACIL_DOCK;
03358 st->build_date = _date;
03359
03360 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
03361
03362 for (CargoID j = 0; j < NUM_CARGO; j++) {
03363 st->goods[j].acceptance_pickup = 0;
03364 st->goods[j].days_since_pickup = 255;
03365 st->goods[j].rating = INITIAL_STATION_RATING;
03366 st->goods[j].last_speed = 0;
03367 st->goods[j].last_age = 255;
03368 }
03369
03370 st->UpdateVirtCoord();
03371 UpdateStationAcceptance(st, false);
03372 st->RecomputeIndustriesNear();
03373 }
03374
03375 void DeleteOilRig(TileIndex tile)
03376 {
03377 Station *st = Station::GetByTile(tile);
03378
03379 MakeWaterKeepingClass(tile, OWNER_NONE);
03380
03381 st->dock_tile = INVALID_TILE;
03382 st->airport.Clear();
03383 st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
03384 st->airport.flags = 0;
03385
03386 st->rect.AfterRemoveTile(st, tile);
03387
03388 st->UpdateVirtCoord();
03389 st->RecomputeIndustriesNear();
03390 if (!st->IsInUse()) delete st;
03391 }
03392
03393 static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
03394 {
03395 if (IsDriveThroughStopTile(tile)) {
03396 for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
03397
03398 if (GetRoadOwner(tile, rt) == old_owner) {
03399 SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
03400 }
03401 }
03402 }
03403
03404 if (!IsTileOwner(tile, old_owner)) return;
03405
03406 if (new_owner != INVALID_OWNER) {
03407
03408 SetTileOwner(tile, new_owner);
03409 InvalidateWindowClassesData(WC_STATION_LIST, 0);
03410 } else {
03411 if (IsDriveThroughStopTile(tile)) {
03412
03413 DoCommand(tile, 1 | 1 << 8, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, DC_EXEC | DC_BANKRUPT, CMD_REMOVE_ROAD_STOP);
03414 assert(IsTileType(tile, MP_ROAD));
03415
03416 ChangeTileOwner(tile, old_owner, new_owner);
03417 } else {
03418 DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
03419
03420
03421
03422 if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
03423 }
03424 }
03425 }
03426
03435 static bool CanRemoveRoadWithStop(TileIndex tile, DoCommandFlag flags)
03436 {
03437
03438 if (_current_company == OWNER_WATER) return true;
03439
03440 RoadTypes rts = GetRoadTypes(tile);
03441 if (HasBit(rts, ROADTYPE_TRAM)) {
03442 Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
03443 if (tram_owner != OWNER_NONE && CheckOwnership(tram_owner).Failed()) return false;
03444 }
03445 if (HasBit(rts, ROADTYPE_ROAD)) {
03446 Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
03447 if (road_owner != OWNER_TOWN) {
03448 if (road_owner != OWNER_NONE && CheckOwnership(road_owner).Failed()) return false;
03449 } else {
03450 if (CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_ROAD), OWNER_TOWN, ROADTYPE_ROAD, flags).Failed()) return false;
03451 }
03452 }
03453
03454 return true;
03455 }
03456
03457 CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
03458 {
03459 if (flags & DC_AUTO) {
03460 switch (GetStationType(tile)) {
03461 default: break;
03462 case STATION_RAIL: return_cmd_error(STR_ERROR_MUST_DEMOLISH_RAILROAD);
03463 case STATION_WAYPOINT: return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
03464 case STATION_AIRPORT: return_cmd_error(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
03465 case STATION_TRUCK: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
03466 case STATION_BUS: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
03467 case STATION_BUOY: return_cmd_error(STR_ERROR_BUOY_IN_THE_WAY);
03468 case STATION_DOCK: return_cmd_error(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
03469 case STATION_OILRIG:
03470 SetDParam(1, STR_INDUSTRY_NAME_OIL_RIG);
03471 return_cmd_error(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY);
03472 }
03473 }
03474
03475 switch (GetStationType(tile)) {
03476 case STATION_RAIL: return RemoveRailStation(tile, flags);
03477 case STATION_WAYPOINT: return RemoveRailWaypoint(tile, flags);
03478 case STATION_AIRPORT: return RemoveAirport(tile, flags);
03479 case STATION_TRUCK:
03480 if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags)) {
03481 return_cmd_error(STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
03482 }
03483 return RemoveRoadStop(tile, flags);
03484 case STATION_BUS:
03485 if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags)) {
03486 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
03487 }
03488 return RemoveRoadStop(tile, flags);
03489 case STATION_BUOY: return RemoveBuoy(tile, flags);
03490 case STATION_DOCK: return RemoveDock(tile, flags);
03491 default: break;
03492 }
03493
03494 return CMD_ERROR;
03495 }
03496
03497 static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
03498 {
03499 if (_settings_game.construction.build_on_slopes && AutoslopeEnabled()) {
03500
03501
03502
03503 if (!IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new))) {
03504 switch (GetStationType(tile)) {
03505 case STATION_WAYPOINT:
03506 case STATION_RAIL: {
03507 DiagDirection direction = AxisToDiagDir(GetRailStationAxis(tile));
03508 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
03509 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
03510 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
03511 }
03512
03513 case STATION_AIRPORT:
03514 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
03515
03516 case STATION_TRUCK:
03517 case STATION_BUS: {
03518 DiagDirection direction = GetRoadStopDir(tile);
03519 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
03520 if (IsDriveThroughStopTile(tile)) {
03521 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
03522 }
03523 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
03524 }
03525
03526 default: break;
03527 }
03528 }
03529 }
03530 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
03531 }
03532
03533
03534 extern const TileTypeProcs _tile_type_station_procs = {
03535 DrawTile_Station,
03536 GetSlopeZ_Station,
03537 ClearTile_Station,
03538 NULL,
03539 GetTileDesc_Station,
03540 GetTileTrackStatus_Station,
03541 ClickTile_Station,
03542 AnimateTile_Station,
03543 TileLoop_Station,
03544 ChangeTileOwner_Station,
03545 NULL,
03546 VehicleEnter_Station,
03547 GetFoundation_Station,
03548 TerraformTile_Station,
03549 };