00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "aircraft.h"
00008 #include "bridge_map.h"
00009 #include "cmd_helper.h"
00010 #include "landscape.h"
00011 #include "viewport_func.h"
00012 #include "command_func.h"
00013 #include "town.h"
00014 #include "news_func.h"
00015 #include "train.h"
00016 #include "roadveh.h"
00017 #include "industry_map.h"
00018 #include "newgrf_station.h"
00019 #include "newgrf_commons.h"
00020 #include "yapf/yapf.h"
00021 #include "road_internal.h"
00022 #include "variables.h"
00023 #include "autoslope.h"
00024 #include "water.h"
00025 #include "station_gui.h"
00026 #include "strings_func.h"
00027 #include "functions.h"
00028 #include "window_func.h"
00029 #include "date_func.h"
00030 #include "vehicle_func.h"
00031 #include "string_func.h"
00032 #include "oldpool_func.h"
00033 #include "animated_tile_func.h"
00034 #include "elrail_func.h"
00035
00036 #include "table/strings.h"
00037
00038 DEFINE_OLD_POOL_GENERIC(Station, Station)
00039 DEFINE_OLD_POOL_GENERIC(RoadStop, RoadStop)
00040
00041
00048 bool IsHangar(TileIndex t)
00049 {
00050 assert(IsTileType(t, MP_STATION));
00051
00052 const Station *st = GetStationByTile(t);
00053 const AirportFTAClass *apc = st->Airport();
00054
00055 for (uint i = 0; i < apc->nof_depots; i++) {
00056 if (st->airport_tile + ToTileIndexDiff(apc->airport_depots[i]) == t) return true;
00057 }
00058
00059 return false;
00060 }
00061
00062 RoadStop *GetRoadStopByTile(TileIndex tile, RoadStopType type)
00063 {
00064 const Station *st = GetStationByTile(tile);
00065
00066 for (RoadStop *rs = st->GetPrimaryRoadStop(type);; rs = rs->next) {
00067 if (rs->xy == tile) return rs;
00068 assert(rs->next != NULL);
00069 }
00070 }
00071
00072
00073 static uint GetNumRoadStopsInStation(const Station *st, RoadStopType type)
00074 {
00075 uint num = 0;
00076
00077 assert(st != NULL);
00078 for (const RoadStop *rs = st->GetPrimaryRoadStop(type); rs != NULL; rs = rs->next) {
00079 num++;
00080 }
00081
00082 return num;
00083 }
00084
00085
00086 #define CHECK_STATIONS_ERR ((Station*)-1)
00087
00088 static Station *GetStationAround(TileIndex tile, int w, int h, StationID closest_station)
00089 {
00090
00091 BEGIN_TILE_LOOP(tile_cur, w + 2, h + 2, tile - TileDiffXY(1, 1))
00092 if (IsTileType(tile_cur, MP_STATION)) {
00093 StationID t = GetStationIndex(tile_cur);
00094
00095 if (closest_station == INVALID_STATION) {
00096 closest_station = t;
00097 } else if (closest_station != t) {
00098 _error_message = STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING;
00099 return CHECK_STATIONS_ERR;
00100 }
00101 }
00102 END_TILE_LOOP(tile_cur, w + 2, h + 2, tile - TileDiffXY(1, 1))
00103 return (closest_station == INVALID_STATION) ? NULL : GetStation(closest_station);
00104 }
00105
00111 typedef bool (*CMSAMatcher)(TileIndex tile);
00112
00121 static int CountMapSquareAround(TileIndex tile, CMSAMatcher cmp)
00122 {
00123 int num = 0;
00124
00125 for (int dx = -3; dx <= 3; dx++) {
00126 for (int dy = -3; dy <= 3; dy++) {
00127 TileIndex t = TileAddWrap(tile, dx, dy);
00128 if (t != INVALID_TILE && cmp(t)) num++;
00129 }
00130 }
00131
00132 return num;
00133 }
00134
00140 static bool CMSAMine(TileIndex tile)
00141 {
00142
00143 if (!IsTileType(tile, MP_INDUSTRY)) return false;
00144
00145 const Industry *ind = GetIndustryByTile(tile);
00146
00147
00148 if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_EXTRACTIVE) == 0) return false;
00149
00150 for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
00151
00152 if (ind->produced_cargo[i] != CT_INVALID && (GetCargo(ind->produced_cargo[i])->classes & CC_LIQUID) == 0) return true;
00153 }
00154
00155 return false;
00156 }
00157
00163 static bool CMSAWater(TileIndex tile)
00164 {
00165 return IsTileType(tile, MP_WATER) && IsWater(tile);
00166 }
00167
00173 static bool CMSATree(TileIndex tile)
00174 {
00175 return IsTileType(tile, MP_TREES);
00176 }
00177
00183 static bool CMSAForest(TileIndex tile)
00184 {
00185
00186 if (!IsTileType(tile, MP_INDUSTRY)) return false;
00187
00188 const Industry *ind = GetIndustryByTile(tile);
00189
00190
00191 if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_ORGANIC) == 0) return false;
00192
00193 for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
00194
00195 if (ind->produced_cargo[i] != CT_INVALID && GetCargo(ind->produced_cargo[i])->label == 'WOOD') return true;
00196 }
00197
00198 return false;
00199 }
00200
00201 #define M(x) ((x) - STR_SV_STNAME)
00202
00203 enum StationNaming {
00204 STATIONNAMING_RAIL = 0,
00205 STATIONNAMING_ROAD = 0,
00206 STATIONNAMING_AIRPORT,
00207 STATIONNAMING_OILRIG,
00208 STATIONNAMING_DOCK,
00209 STATIONNAMING_BUOY,
00210 STATIONNAMING_HELIPORT,
00211 };
00212
00214 struct StationNameInformation {
00215 uint32 free_names;
00216 bool *indtypes;
00217 };
00218
00227 static bool FindNearIndustryName(TileIndex tile, void *user_data)
00228 {
00229
00230 StationNameInformation *sni = (StationNameInformation*)user_data;
00231 if (!IsTileType(tile, MP_INDUSTRY)) return false;
00232
00233
00234 IndustryType indtype = GetIndustryType(tile);
00235 if (GetIndustrySpec(indtype)->station_name == STR_UNDEFINED) return false;
00236
00237
00238
00239 sni->free_names &= ~(1 << M(STR_SV_STNAME_OILFIELD) | 1 << M(STR_SV_STNAME_MINES));
00240 return !sni->indtypes[indtype];
00241 }
00242
00243 static StringID GenerateStationName(Station *st, TileIndex tile, int flag)
00244 {
00245 static const uint32 _gen_station_name_bits[] = {
00246 0,
00247 1 << M(STR_SV_STNAME_AIRPORT),
00248 1 << M(STR_SV_STNAME_OILFIELD),
00249 1 << M(STR_SV_STNAME_DOCKS),
00250 0x1FF << M(STR_SV_STNAME_BUOY_1),
00251 1 << M(STR_SV_STNAME_HELIPORT),
00252 };
00253
00254 const Town *t = st->town;
00255 uint32 free_names = UINT32_MAX;
00256
00257 bool indtypes[NUM_INDUSTRYTYPES];
00258 memset(indtypes, 0, sizeof(indtypes));
00259
00260 const Station *s;
00261 FOR_ALL_STATIONS(s) {
00262 if (s != st && s->town == t) {
00263 if (s->indtype != IT_INVALID) {
00264 indtypes[s->indtype] = true;
00265 continue;
00266 }
00267 uint str = M(s->string_id);
00268 if (str <= 0x20) {
00269 if (str == M(STR_SV_STNAME_FOREST)) {
00270 str = M(STR_SV_STNAME_WOODS);
00271 }
00272 ClrBit(free_names, str);
00273 }
00274 }
00275 }
00276
00277 if (flag != STATIONNAMING_BUOY) {
00278 TileIndex indtile = tile;
00279 StationNameInformation sni = { free_names, indtypes };
00280 if (CircularTileSearch(&indtile, 7, FindNearIndustryName, &sni)) {
00281
00282 IndustryType indtype = GetIndustryType(indtile);
00283 const IndustrySpec *indsp = GetIndustrySpec(indtype);
00284
00285 if (indsp->station_name != STR_NULL) {
00286 st->indtype = indtype;
00287 return STR_SV_STNAME_FALLBACK;
00288 }
00289 }
00290
00291
00292 free_names = sni.free_names;
00293 }
00294
00295
00296 uint32 tmp = free_names & _gen_station_name_bits[flag];
00297 if (tmp != 0) return STR_SV_STNAME + FindFirstBit(tmp);
00298
00299
00300 if (HasBit(free_names, M(STR_SV_STNAME_MINES))) {
00301 if (CountMapSquareAround(tile, CMSAMine) >= 2) {
00302 return STR_SV_STNAME_MINES;
00303 }
00304 }
00305
00306
00307 if (DistanceMax(tile, t->xy) < 8) {
00308 if (HasBit(free_names, M(STR_SV_STNAME))) return STR_SV_STNAME;
00309
00310 if (HasBit(free_names, M(STR_SV_STNAME_CENTRAL))) return STR_SV_STNAME_CENTRAL;
00311 }
00312
00313
00314 if (HasBit(free_names, M(STR_SV_STNAME_LAKESIDE)) &&
00315 DistanceFromEdge(tile) < 20 &&
00316 CountMapSquareAround(tile, CMSAWater) >= 5) {
00317 return STR_SV_STNAME_LAKESIDE;
00318 }
00319
00320
00321 if (HasBit(free_names, M(STR_SV_STNAME_WOODS)) && (
00322 CountMapSquareAround(tile, CMSATree) >= 8 ||
00323 CountMapSquareAround(tile, CMSAForest) >= 2)
00324 ) {
00325 return _settings_game.game_creation.landscape == LT_TROPIC ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
00326 }
00327
00328
00329 uint z = GetTileZ(tile);
00330 uint z2 = GetTileZ(t->xy);
00331 if (z < z2) {
00332 if (HasBit(free_names, M(STR_SV_STNAME_VALLEY))) return STR_SV_STNAME_VALLEY;
00333 } else if (z > z2) {
00334 if (HasBit(free_names, M(STR_SV_STNAME_HEIGHTS))) return STR_SV_STNAME_HEIGHTS;
00335 }
00336
00337
00338 static const int8 _direction_and_table[] = {
00339 ~( (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00340 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00341 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00342 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) ),
00343 };
00344
00345 free_names &= _direction_and_table[
00346 (TileX(tile) < TileX(t->xy)) +
00347 (TileY(tile) < TileY(t->xy)) * 2];
00348
00349 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));
00350 return (tmp == 0) ? STR_SV_STNAME_FALLBACK : (STR_SV_STNAME + FindFirstBit(tmp));
00351 }
00352 #undef M
00353
00359 static Station *GetClosestDeletedStation(TileIndex tile)
00360 {
00361 uint threshold = 8;
00362 Station *best_station = NULL;
00363 Station *st;
00364
00365 FOR_ALL_STATIONS(st) {
00366 if (st->facilities == 0 && st->owner == _current_company) {
00367 uint cur_dist = DistanceManhattan(tile, st->xy);
00368
00369 if (cur_dist < threshold) {
00370 threshold = cur_dist;
00371 best_station = st;
00372 }
00373 }
00374 }
00375
00376 return best_station;
00377 }
00378
00382 static void UpdateStationVirtCoord(Station *st)
00383 {
00384 Point pt = RemapCoords2(TileX(st->xy) * TILE_SIZE, TileY(st->xy) * TILE_SIZE);
00385
00386 pt.y -= 32;
00387 if (st->facilities & FACIL_AIRPORT && st->airport_type == AT_OILRIG) pt.y -= 16;
00388
00389 SetDParam(0, st->index);
00390 SetDParam(1, st->facilities);
00391 UpdateViewportSignPos(&st->sign, pt.x, pt.y, STR_305C_0);
00392 }
00393
00395 void UpdateAllStationVirtCoord()
00396 {
00397 Station *st;
00398
00399 FOR_ALL_STATIONS(st) {
00400 UpdateStationVirtCoord(st);
00401 }
00402 }
00403
00412 static void UpdateStationVirtCoordDirty(Station *st)
00413 {
00414 st->MarkDirty();
00415 UpdateStationVirtCoord(st);
00416 st->MarkDirty();
00417 }
00418
00423 static uint GetAcceptanceMask(const Station *st)
00424 {
00425 uint mask = 0;
00426
00427 for (CargoID i = 0; i < NUM_CARGO; i++) {
00428 if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) mask |= 1 << i;
00429 }
00430 return mask;
00431 }
00432
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, GetCargo(cargo[i])->name);
00440 }
00441
00442 SetDParam(0, st->index);
00443 AddNewsItem(msg, NS_ACCEPTANCE, st->xy, st->index);
00444 }
00445
00454 void GetProductionAroundTiles(AcceptedCargo produced, TileIndex tile,
00455 int w, int h, int rad)
00456 {
00457 memset(produced, 0, sizeof(AcceptedCargo));
00458
00459 int x = TileX(tile);
00460 int y = TileY(tile);
00461
00462
00463
00464 int x2 = min(x + w + rad, MapSizeX());
00465 int x1 = max(x - rad, 0);
00466
00467 int y2 = min(y + h + rad, MapSizeY());
00468 int y1 = max(y - rad, 0);
00469
00470 assert(x1 < x2);
00471 assert(y1 < y2);
00472 assert(w > 0);
00473 assert(h > 0);
00474
00475 for (int yc = y1; yc != y2; yc++) {
00476 for (int xc = x1; xc != x2; xc++) {
00477 TileIndex tile = TileXY(xc, yc);
00478
00479 if (!IsTileType(tile, MP_STATION)) {
00480 GetProducedCargoProc *gpc = _tile_type_procs[GetTileType(tile)]->get_produced_cargo_proc;
00481 if (gpc != NULL) {
00482 CargoID cargos[256];
00483 memset(cargos, CT_INVALID, sizeof(cargos));
00484
00485 gpc(tile, cargos);
00486 for (uint i = 0; i < lengthof(cargos); ++i) {
00487 if (cargos[i] != CT_INVALID) produced[cargos[i]]++;
00488 }
00489 }
00490 }
00491 }
00492 }
00493 }
00494
00503 void GetAcceptanceAroundTiles(AcceptedCargo accepts, TileIndex tile,
00504 int w, int h, int rad)
00505 {
00506 memset(accepts, 0, sizeof(AcceptedCargo));
00507
00508 int x = TileX(tile);
00509 int y = TileY(tile);
00510
00511
00512
00513 int x2 = min(x + w + rad, MapSizeX());
00514 int y2 = min(y + h + rad, MapSizeY());
00515 int x1 = max(x - rad, 0);
00516 int y1 = max(y - rad, 0);
00517
00518 assert(x1 < x2);
00519 assert(y1 < y2);
00520 assert(w > 0);
00521 assert(h > 0);
00522
00523 for (int yc = y1; yc != y2; yc++) {
00524 for (int xc = x1; xc != x2; xc++) {
00525 TileIndex tile = TileXY(xc, yc);
00526
00527 if (!IsTileType(tile, MP_STATION)) {
00528 AcceptedCargo ac;
00529
00530 GetAcceptedCargo(tile, ac);
00531 for (uint i = 0; i < lengthof(ac); ++i) accepts[i] += ac[i];
00532 }
00533 }
00534 }
00535 }
00536
00537 static inline void MergePoint(Rect *rect, TileIndex tile)
00538 {
00539 int x = TileX(tile);
00540 int y = TileY(tile);
00541
00542 if (rect->left > x) rect->left = x;
00543 if (rect->bottom > y) rect->bottom = y;
00544 if (rect->right < x) rect->right = x;
00545 if (rect->top < y) rect->top = y;
00546 }
00547
00552 static void UpdateStationAcceptance(Station *st, bool show_msg)
00553 {
00554
00555 if (st->IsBuoy()) return;
00556
00557 Rect rect;
00558 rect.left = MapSizeX();
00559 rect.bottom = MapSizeY();
00560 rect.right = 0;
00561 rect.top = 0;
00562
00563
00564 uint old_acc = GetAcceptanceMask(st);
00565
00566
00567 if (st->train_tile != INVALID_TILE) {
00568 MergePoint(&rect, st->train_tile);
00569 MergePoint(&rect, st->train_tile + TileDiffXY(st->trainst_w - 1, st->trainst_h - 1));
00570 }
00571
00572 if (st->airport_tile != INVALID_TILE) {
00573 const AirportFTAClass *afc = st->Airport();
00574
00575 MergePoint(&rect, st->airport_tile);
00576 MergePoint(&rect, st->airport_tile + TileDiffXY(afc->size_x - 1, afc->size_y - 1));
00577 }
00578
00579 if (st->dock_tile != INVALID_TILE) {
00580 MergePoint(&rect, st->dock_tile);
00581 if (IsDockTile(st->dock_tile)) {
00582 MergePoint(&rect, st->dock_tile + TileOffsByDiagDir(GetDockDirection(st->dock_tile)));
00583 }
00584 }
00585
00586 for (const RoadStop *rs = st->bus_stops; rs != NULL; rs = rs->next) {
00587 MergePoint(&rect, rs->xy);
00588 }
00589
00590 for (const RoadStop *rs = st->truck_stops; rs != NULL; rs = rs->next) {
00591 MergePoint(&rect, rs->xy);
00592 }
00593
00594
00595 AcceptedCargo accepts;
00596 assert((rect.right >= rect.left) == !st->rect.IsEmpty());
00597 if (rect.right >= rect.left) {
00598 assert(rect.left == st->rect.left);
00599 assert(rect.top == st->rect.bottom);
00600 assert(rect.right == st->rect.right);
00601 assert(rect.bottom == st->rect.top);
00602 GetAcceptanceAroundTiles(
00603 accepts,
00604 TileXY(rect.left, rect.bottom),
00605 rect.right - rect.left + 1,
00606 rect.top - rect.bottom + 1,
00607 st->GetCatchmentRadius()
00608 );
00609 } else {
00610 memset(accepts, 0, sizeof(accepts));
00611 }
00612
00613
00614 for (CargoID i = 0; i < NUM_CARGO; i++) {
00615 uint amt = min(accepts[i], 15);
00616
00617
00618 bool is_passengers = IsCargoInClass(i, CC_PASSENGERS);
00619 if ((!is_passengers && !(st->facilities & (byte)~FACIL_BUS_STOP)) ||
00620 (is_passengers && !(st->facilities & (byte)~FACIL_TRUCK_STOP))) {
00621 amt = 0;
00622 }
00623
00624 SB(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, amt >= 8);
00625 }
00626
00627
00628 uint new_acc = GetAcceptanceMask(st);
00629 if (old_acc == new_acc) return;
00630
00631
00632 if (show_msg && st->owner == _local_company && st->facilities) {
00633
00634
00635 static const StringID accept_msg[] = {
00636 STR_3040_NOW_ACCEPTS,
00637 STR_3041_NOW_ACCEPTS_AND,
00638 };
00639 static const StringID reject_msg[] = {
00640 STR_303E_NO_LONGER_ACCEPTS,
00641 STR_303F_NO_LONGER_ACCEPTS_OR,
00642 };
00643
00644
00645 CargoID accepts[2] = { CT_INVALID, CT_INVALID };
00646 CargoID rejects[2] = { CT_INVALID, CT_INVALID };
00647 uint num_acc = 0;
00648 uint num_rej = 0;
00649
00650
00651 for (CargoID i = 0; i < NUM_CARGO; i++) {
00652 if (HasBit(new_acc, i)) {
00653 if (!HasBit(old_acc, i) && num_acc < lengthof(accepts)) {
00654
00655 accepts[num_acc++] = i;
00656 }
00657 } else {
00658 if (HasBit(old_acc, i) && num_rej < lengthof(rejects)) {
00659
00660 rejects[num_rej++] = i;
00661 }
00662 }
00663 }
00664
00665
00666 if (num_acc > 0) ShowRejectOrAcceptNews(st, num_acc, accepts, accept_msg[num_acc - 1]);
00667 if (num_rej > 0) ShowRejectOrAcceptNews(st, num_rej, rejects, reject_msg[num_rej - 1]);
00668 }
00669
00670
00671 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_ACCEPTLIST);
00672 }
00673
00674 static void UpdateStationSignCoord(Station *st)
00675 {
00676 const StationRect *r = &st->rect;
00677
00678 if (r->IsEmpty()) return;
00679
00680
00681 st->xy = TileXY(ClampU(TileX(st->xy), r->left, r->right), ClampU(TileY(st->xy), r->top, r->bottom));
00682 UpdateStationVirtCoordDirty(st);
00683 }
00684
00690 static void DeleteStationIfEmpty(Station *st)
00691 {
00692 if (st->facilities == 0) {
00693 st->delete_ctr = 0;
00694 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
00695 }
00696
00697 UpdateStationSignCoord(st);
00698 }
00699
00700 static CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags);
00701
00712 CommandCost CheckFlatLandBelow(TileIndex tile, uint w, uint h, DoCommandFlag flags, uint invalid_dirs, StationID *station, bool check_clear = true)
00713 {
00714 CommandCost cost(EXPENSES_CONSTRUCTION);
00715 int allowed_z = -1;
00716
00717 BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
00718 if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) {
00719 return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
00720 }
00721
00722 if (!EnsureNoVehicleOnGround(tile_cur)) return CMD_ERROR;
00723
00724 uint z;
00725 Slope tileh = GetTileSlope(tile_cur, &z);
00726
00727
00728
00729
00730
00731 if (IsSteepSlope(tileh) ||
00732 ((!_settings_game.construction.build_on_slopes) && tileh != SLOPE_FLAT)) {
00733 return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
00734 }
00735
00736 int flat_z = z;
00737 if (tileh != SLOPE_FLAT) {
00738
00739
00740 if ((HasBit(invalid_dirs, DIAGDIR_NE) && !(tileh & SLOPE_NE)) ||
00741 (HasBit(invalid_dirs, DIAGDIR_SE) && !(tileh & SLOPE_SE)) ||
00742 (HasBit(invalid_dirs, DIAGDIR_SW) && !(tileh & SLOPE_SW)) ||
00743 (HasBit(invalid_dirs, DIAGDIR_NW) && !(tileh & SLOPE_NW))) {
00744 return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
00745 }
00746 cost.AddCost(_price.terraform);
00747 flat_z += TILE_HEIGHT;
00748 }
00749
00750
00751 if (allowed_z == -1) {
00752
00753 allowed_z = flat_z;
00754 } else if (allowed_z != flat_z) {
00755 return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
00756 }
00757
00758
00759
00760
00761 if (station != NULL && IsTileType(tile_cur, MP_STATION)) {
00762 if (!IsRailwayStation(tile_cur)) {
00763 return ClearTile_Station(tile_cur, DC_AUTO);
00764 } else {
00765 StationID st = GetStationIndex(tile_cur);
00766 if (*station == INVALID_STATION) {
00767 *station = st;
00768 } else if (*station != st) {
00769 return_cmd_error(STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING);
00770 }
00771 }
00772 } else if (check_clear) {
00773 CommandCost ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00774 if (CmdFailed(ret)) return ret;
00775 cost.AddCost(ret);
00776 }
00777 } END_TILE_LOOP(tile_cur, w, h, tile)
00778
00779 return cost;
00780 }
00781
00782 static bool CanExpandRailroadStation(const Station *st, uint *fin, Axis axis)
00783 {
00784 uint curw = st->trainst_w;
00785 uint curh = st->trainst_h;
00786 TileIndex tile = fin[0];
00787 uint w = fin[1];
00788 uint h = fin[2];
00789
00790 if (_settings_game.station.nonuniform_stations) {
00791
00792 int x = min(TileX(st->train_tile), TileX(tile));
00793 int y = min(TileY(st->train_tile), TileY(tile));
00794 curw = max(TileX(st->train_tile) + curw, TileX(tile) + w) - x;
00795 curh = max(TileY(st->train_tile) + curh, TileY(tile) + h) - y;
00796 tile = TileXY(x, y);
00797 } else {
00798
00799
00800 BEGIN_TILE_LOOP(t, st->trainst_w, st->trainst_h, st->train_tile)
00801 if (!st->TileBelongsToRailStation(t)) {
00802 _error_message = STR_NONUNIFORM_STATIONS_DISALLOWED;
00803 return false;
00804 }
00805 END_TILE_LOOP(t, st->trainst_w, st->trainst_h, st->train_tile)
00806
00807
00808 if (GetRailStationAxis(st->train_tile) != axis) {
00809 _error_message = STR_NONUNIFORM_STATIONS_DISALLOWED;
00810 return false;
00811 }
00812
00813
00814 if (curw == w && st->train_tile == tile + TileDiffXY(0, h)) {
00815
00816 curh += h;
00817 } else if (curw == w && st->train_tile == tile - TileDiffXY(0, curh)) {
00818
00819 tile -= TileDiffXY(0, curh);
00820 curh += h;
00821 } else if (curh == h && st->train_tile == tile + TileDiffXY(w, 0)) {
00822
00823 curw += w;
00824 } else if (curh == h && st->train_tile == tile - TileDiffXY(curw, 0)) {
00825
00826 tile -= TileDiffXY(curw, 0);
00827 curw += w;
00828 } else {
00829 _error_message = STR_NONUNIFORM_STATIONS_DISALLOWED;
00830 return false;
00831 }
00832 }
00833
00834 if (curw > _settings_game.station.station_spread || curh > _settings_game.station.station_spread) {
00835 _error_message = STR_306C_STATION_TOO_SPREAD_OUT;
00836 return false;
00837 }
00838
00839
00840
00841 fin[0] = tile;
00842 fin[1] = curw;
00843 fin[2] = curh;
00844 return true;
00845 }
00846
00847 static inline byte *CreateSingle(byte *layout, int n)
00848 {
00849 int i = n;
00850 do *layout++ = 0; while (--i);
00851 layout[((n - 1) >> 1) - n] = 2;
00852 return layout;
00853 }
00854
00855 static inline byte *CreateMulti(byte *layout, int n, byte b)
00856 {
00857 int i = n;
00858 do *layout++ = b; while (--i);
00859 if (n > 4) {
00860 layout[0 - n] = 0;
00861 layout[n - 1 - n] = 0;
00862 }
00863 return layout;
00864 }
00865
00866 static void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec)
00867 {
00868 if (statspec != NULL && statspec->lengths >= plat_len &&
00869 statspec->platforms[plat_len - 1] >= numtracks &&
00870 statspec->layouts[plat_len - 1][numtracks - 1]) {
00871
00872 memcpy(layout, statspec->layouts[plat_len - 1][numtracks - 1],
00873 plat_len * numtracks);
00874 return;
00875 }
00876
00877 if (plat_len == 1) {
00878 CreateSingle(layout, numtracks);
00879 } else {
00880 if (numtracks & 1) layout = CreateSingle(layout, plat_len);
00881 numtracks >>= 1;
00882
00883 while (--numtracks >= 0) {
00884 layout = CreateMulti(layout, plat_len, 4);
00885 layout = CreateMulti(layout, plat_len, 6);
00886 }
00887 }
00888 }
00889
00904 CommandCost CmdBuildRailroadStation(TileIndex tile_org, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00905 {
00906
00907 if (!CheckIfAuthorityAllowsNewStation(tile_org, flags)) return CMD_ERROR;
00908 if (!ValParamRailtype((RailType)(p1 & 0xF))) return CMD_ERROR;
00909
00910
00911 Axis axis = Extract<Axis, 4>(p1);
00912 uint numtracks = GB(p1, 8, 8);
00913 uint plat_len = GB(p1, 16, 8);
00914
00915 int w_org, h_org;
00916 if (axis == AXIS_X) {
00917 w_org = plat_len;
00918 h_org = numtracks;
00919 } else {
00920 h_org = plat_len;
00921 w_org = numtracks;
00922 }
00923
00924 StationID station_to_join = GB(p2, 16, 16);
00925 bool distant_join = (station_to_join != INVALID_STATION);
00926
00927 if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR;
00928
00929 if (h_org > _settings_game.station.station_spread || w_org > _settings_game.station.station_spread) return CMD_ERROR;
00930
00931
00932 uint finalvalues[3];
00933 finalvalues[0] = tile_org;
00934 finalvalues[1] = w_org;
00935 finalvalues[2] = h_org;
00936
00937
00938 StationID est = INVALID_STATION;
00939
00940
00941
00942 CommandCost ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags & ~DC_EXEC, 5 << axis, _settings_game.station.nonuniform_stations ? &est : NULL);
00943 if (CmdFailed(ret)) return ret;
00944 CommandCost cost(EXPENSES_CONSTRUCTION, ret.GetCost() + (numtracks * _price.train_station_track + _price.train_station_length) * plat_len);
00945
00946 Station *st = NULL;
00947 bool check_surrounding = true;
00948
00949 if (_settings_game.station.adjacent_stations) {
00950 if (est != INVALID_STATION) {
00951 if (HasBit(p1, 24) && est != station_to_join) {
00952
00953
00954 return_cmd_error(STR_MUST_REMOVE_RAILWAY_STATION_FIRST);
00955 } else {
00956
00957
00958 st = GetStation(est);
00959 check_surrounding = false;
00960 }
00961 } else {
00962
00963
00964 if (HasBit(p1, 24)) check_surrounding = false;
00965 }
00966 }
00967
00968 if (check_surrounding) {
00969
00970 st = GetStationAround(tile_org, w_org, h_org, est);
00971 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
00972 }
00973
00974
00975 if (st == NULL && distant_join) st = GetStation(station_to_join);
00976
00977
00978 if (st == NULL) st = GetClosestDeletedStation(tile_org);
00979
00980 if (st != NULL) {
00981
00982 if (st->owner != _current_company)
00983 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
00984
00985 if (st->train_tile != INVALID_TILE) {
00986
00987 if (!_settings_game.station.join_stations)
00988 return_cmd_error(STR_3005_TOO_CLOSE_TO_ANOTHER_RAILROAD);
00989 if (!CanExpandRailroadStation(st, finalvalues, axis))
00990 return CMD_ERROR;
00991 }
00992
00993
00994 if (!st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TEST)) return CMD_ERROR;
00995 } else {
00996
00997 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
00998
00999 if (flags & DC_EXEC) {
01000 st = new Station(tile_org);
01001
01002 st->town = ClosestTownFromTile(tile_org, UINT_MAX);
01003 st->string_id = GenerateStationName(st, tile_org, STATIONNAMING_RAIL);
01004
01005 if (IsValidCompanyID(_current_company)) {
01006 SetBit(st->town->have_ratings, _current_company);
01007 }
01008 }
01009 }
01010
01011
01012 if (GB(p2, 0, 8) >= GetNumStationClasses()) return CMD_ERROR;
01013
01014
01015 const StationSpec *statspec = GetCustomStationSpec((StationClassID)GB(p2, 0, 8), GB(p2, 8, 8));
01016 int specindex = AllocateSpecToStation(statspec, st, (flags & DC_EXEC) != 0);
01017 if (specindex == -1) return_cmd_error(STR_TOO_MANY_STATION_SPECS);
01018
01019 if (statspec != NULL) {
01020
01021
01022
01023 if (HasBit(statspec->disallowed_platforms, numtracks - 1) || HasBit(statspec->disallowed_lengths, plat_len - 1)) {
01024 return CMD_ERROR;
01025 }
01026
01027
01028 if (HasBit(statspec->callbackmask, CBM_STATION_AVAIL) && GB(GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE), 0, 8) == 0) {
01029 return CMD_ERROR;
01030 }
01031 }
01032
01033 if (flags & DC_EXEC) {
01034 TileIndexDiff tile_delta;
01035 byte *layout_ptr;
01036 byte numtracks_orig;
01037 Track track;
01038
01039
01040
01041
01042 ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags, 5 << axis, _settings_game.station.nonuniform_stations ? &est : NULL);
01043 if (CmdFailed(ret)) return ret;
01044
01045 st->train_tile = finalvalues[0];
01046 st->AddFacility(FACIL_TRAIN, finalvalues[0]);
01047
01048 st->trainst_w = finalvalues[1];
01049 st->trainst_h = finalvalues[2];
01050
01051 st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
01052
01053 if (statspec != NULL) {
01054
01055
01056 st->cached_anim_triggers |= statspec->anim_triggers;
01057 }
01058
01059 tile_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
01060 track = AxisToTrack(axis);
01061
01062 layout_ptr = AllocaM(byte, numtracks * plat_len);
01063 GetStationLayout(layout_ptr, numtracks, plat_len, statspec);
01064
01065 numtracks_orig = numtracks;
01066
01067 SmallVector<Vehicle*, 4> affected_vehicles;
01068 do {
01069 TileIndex tile = tile_org;
01070 int w = plat_len;
01071 do {
01072 byte layout = *layout_ptr++;
01073 if (IsRailwayStationTile(tile) && GetRailwayStationReservation(tile)) {
01074
01075 Vehicle *v = GetTrainForReservation(tile, AxisToTrack(GetRailStationAxis(tile)));
01076 if (v != NULL) {
01077 FreeTrainTrackReservation(v);
01078 *affected_vehicles.Append() = v;
01079 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(GetVehicleTrackdir(v)), false);
01080 for (; v->Next() != NULL; v = v->Next()) ;
01081 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(GetVehicleTrackdir(v))), false);
01082 }
01083 }
01084
01085 byte old_specindex = IsTileType(tile, MP_STATION) ? GetCustomStationSpecIndex(tile) : 0;
01086 MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, (RailType)GB(p1, 0, 4));
01087
01088 DeallocateSpecFromStation(st, old_specindex);
01089
01090 SetCustomStationSpecIndex(tile, specindex);
01091 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
01092 SetStationAnimationFrame(tile, 0);
01093
01094 if (statspec != NULL) {
01095
01096 uint32 platinfo = GetPlatformInfo(AXIS_X, 0, plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
01097
01098
01099 uint16 callback = GetStationCallback(CBID_STATION_TILE_LAYOUT, platinfo, 0, statspec, NULL, tile);
01100 if (callback != CALLBACK_FAILED && callback < 8) SetStationGfx(tile, (callback & ~1) + axis);
01101
01102
01103 StationAnimationTrigger(st, tile, STAT_ANIM_BUILT);
01104 }
01105
01106 tile += tile_delta;
01107 } while (--w);
01108 AddTrackToSignalBuffer(tile_org, track, _current_company);
01109 YapfNotifyTrackLayoutChange(tile_org, track);
01110 tile_org += tile_delta ^ TileDiffXY(1, 1);
01111 } while (--numtracks);
01112
01113 for (uint i = 0; i < affected_vehicles.Length(); ++i) {
01114
01115 Vehicle *v = affected_vehicles[i];
01116 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(GetVehicleTrackdir(v)), true);
01117 TryPathReserve(v, true, true);
01118 for (; v->Next() != NULL; v = v->Next()) ;
01119 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(GetVehicleTrackdir(v))), true);
01120 }
01121
01122 st->MarkTilesDirty(false);
01123 UpdateStationVirtCoordDirty(st);
01124 UpdateStationAcceptance(st, false);
01125 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
01126 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
01127 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_TRAINS);
01128 }
01129
01130 return cost;
01131 }
01132
01133 static void MakeRailwayStationAreaSmaller(Station *st)
01134 {
01135 uint w = st->trainst_w;
01136 uint h = st->trainst_h;
01137 TileIndex tile = st->train_tile;
01138
01139 restart:
01140
01141
01142 if (w != 0 && h != 0) {
01143
01144 for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(0, i));) {
01145
01146 if (++i == h) {
01147 tile += TileDiffXY(1, 0);
01148 w--;
01149 goto restart;
01150 }
01151 }
01152
01153
01154 for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(w - 1, i));) {
01155
01156 if (++i == h) {
01157 w--;
01158 goto restart;
01159 }
01160 }
01161
01162
01163 for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(i, 0));) {
01164
01165 if (++i == w) {
01166 tile += TileDiffXY(0, 1);
01167 h--;
01168 goto restart;
01169 }
01170 }
01171
01172
01173 for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(i, h - 1));) {
01174
01175 if (++i == w) {
01176 h--;
01177 goto restart;
01178 }
01179 }
01180 } else {
01181 tile = INVALID_TILE;
01182 }
01183
01184 st->trainst_w = w;
01185 st->trainst_h = h;
01186 st->train_tile = tile;
01187 }
01188
01196 CommandCost CmdRemoveFromRailroadStation(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01197 {
01198 TileIndex start = p1 == 0 ? tile : p1;
01199
01200
01201 int quantity = 0;
01202
01203 if (tile >= MapSize() || start >= MapSize()) return CMD_ERROR;
01204
01205
01206 int ex = TileX(tile);
01207 int ey = TileY(tile);
01208 int sx = TileX(start);
01209 int sy = TileY(start);
01210 if (ex < sx) Swap(ex, sx);
01211 if (ey < sy) Swap(ey, sy);
01212 tile = TileXY(sx, sy);
01213
01214 int size_x = ex - sx + 1;
01215 int size_y = ey - sy + 1;
01216
01217
01218 BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) {
01219
01220 if (!IsTileType(tile2, MP_STATION) || !IsRailwayStation(tile2)) {
01221 continue;
01222 }
01223
01224
01225 if (!EnsureNoVehicleOnGround(tile2)) {
01226 continue;
01227 }
01228
01229
01230 Station *st = GetStationByTile(tile2);
01231 if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) {
01232 continue;
01233 }
01234
01235
01236
01237
01238 if (!_settings_game.station.nonuniform_stations) return_cmd_error(STR_NONUNIFORM_STATIONS_DISALLOWED);
01239
01240
01241 quantity++;
01242
01243 if (flags & DC_EXEC) {
01244
01245 uint specindex = GetCustomStationSpecIndex(tile2);
01246 Track track = GetRailStationTrack(tile2);
01247 Owner owner = GetTileOwner(tile2);
01248 Vehicle *v = NULL;
01249
01250 if (GetRailwayStationReservation(tile2)) {
01251 v = GetTrainForReservation(tile2, track);
01252 if (v != NULL) {
01253
01254 FreeTrainTrackReservation(v);
01255 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(GetVehicleTrackdir(v)), false);
01256 Vehicle *temp = v;
01257 for (; temp->Next() != NULL; temp = temp->Next()) ;
01258 if (IsRailwayStationTile(temp->tile)) SetRailwayStationPlatformReservation(temp->tile, TrackdirToExitdir(ReverseTrackdir(GetVehicleTrackdir(temp))), false);
01259 }
01260 }
01261
01262 DoClearSquare(tile2);
01263 st->rect.AfterRemoveTile(st, tile2);
01264 AddTrackToSignalBuffer(tile2, track, owner);
01265 YapfNotifyTrackLayoutChange(tile2, track);
01266
01267 DeallocateSpecFromStation(st, specindex);
01268
01269
01270
01271
01272 MakeRailwayStationAreaSmaller(st);
01273 st->MarkTilesDirty(false);
01274 UpdateStationSignCoord(st);
01275
01276 if (v != NULL) {
01277
01278 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(GetVehicleTrackdir(v)), true);
01279 TryPathReserve(v, true, true);
01280 for (; v->Next() != NULL; v = v->Next()) ;
01281 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(GetVehicleTrackdir(v))), true);
01282 }
01283
01284
01285 if (st->train_tile == INVALID_TILE) {
01286 st->facilities &= ~FACIL_TRAIN;
01287 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_TRAINS);
01288 UpdateStationVirtCoordDirty(st);
01289 DeleteStationIfEmpty(st);
01290 }
01291 }
01292 } END_TILE_LOOP(tile2, size_x, size_y, tile)
01293
01294
01295 if (quantity == 0) return CMD_ERROR;
01296
01297 return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_rail_station * quantity);
01298 }
01299
01300
01301 static CommandCost RemoveRailroadStation(Station *st, TileIndex tile, DoCommandFlag flags)
01302 {
01303
01304 if (_current_company == OWNER_WATER && _settings_game.station.nonuniform_stations) {
01305 return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAILROAD_STATION);
01306 }
01307
01308
01309 if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) return CMD_ERROR;
01310
01311
01312 tile = st->train_tile;
01313 int w = st->trainst_w;
01314 int h = st->trainst_h;
01315
01316 assert(w != 0 && h != 0);
01317
01318 CommandCost cost(EXPENSES_CONSTRUCTION);
01319
01320 do {
01321 int w_bak = w;
01322 do {
01323
01324 if (st->TileBelongsToRailStation(tile)) {
01325 if (!EnsureNoVehicleOnGround(tile))
01326 return CMD_ERROR;
01327 cost.AddCost(_price.remove_rail_station);
01328 if (flags & DC_EXEC) {
01329
01330 Track track = GetRailStationTrack(tile);
01331 Owner owner = GetTileOwner(tile);
01332 Vehicle *v = NULL;
01333 if (GetRailwayStationReservation(tile)) {
01334 v = GetTrainForReservation(tile, track);
01335 if (v != NULL) FreeTrainTrackReservation(v);
01336 }
01337 DoClearSquare(tile);
01338 AddTrackToSignalBuffer(tile, track, owner);
01339 YapfNotifyTrackLayoutChange(tile, track);
01340 if (v != NULL) TryPathReserve(v, true);
01341 }
01342 }
01343 tile += TileDiffXY(1, 0);
01344 } while (--w);
01345 w = w_bak;
01346 tile += TileDiffXY(-w, 1);
01347 } while (--h);
01348
01349 if (flags & DC_EXEC) {
01350 st->rect.AfterRemoveRect(st, st->train_tile, st->trainst_w, st->trainst_h);
01351
01352 st->train_tile = INVALID_TILE;
01353 st->trainst_w = st->trainst_h = 0;
01354 st->facilities &= ~FACIL_TRAIN;
01355
01356 free(st->speclist);
01357 st->num_specs = 0;
01358 st->speclist = NULL;
01359 st->cached_anim_triggers = 0;
01360
01361 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_TRAINS);
01362 UpdateStationVirtCoordDirty(st);
01363 DeleteStationIfEmpty(st);
01364 }
01365
01366 return cost;
01367 }
01368
01374 static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
01375 {
01376 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
01377
01378 if (*primary_stop == NULL) {
01379
01380 return primary_stop;
01381 } else {
01382
01383 RoadStop *stop = *primary_stop;
01384 while (stop->next != NULL) stop = stop->next;
01385 return &stop->next;
01386 }
01387 }
01388
01399 CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01400 {
01401 bool type = HasBit(p2, 0);
01402 bool is_drive_through = HasBit(p2, 1);
01403 bool build_over_road = is_drive_through && IsNormalRoadTile(tile);
01404 RoadTypes rts = (RoadTypes)GB(p2, 2, 2);
01405 StationID station_to_join = GB(p2, 16, 16);
01406 bool distant_join = (station_to_join != INVALID_STATION);
01407 Owner tram_owner = _current_company;
01408 Owner road_owner = _current_company;
01409
01410 if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR;
01411
01412 if (!AreValidRoadTypes(rts) || !HasRoadTypesAvail(_current_company, rts)) return CMD_ERROR;
01413
01414
01415 if (!is_drive_through && HasBit(rts, ROADTYPE_TRAM)) return CMD_ERROR;
01416
01417
01418 if (!IsValidDiagDirection((DiagDirection)p1)) return CMD_ERROR;
01419
01420 if (is_drive_through && !IsValidAxis((Axis)p1)) return CMD_ERROR;
01421
01422 if (build_over_road && (GetAllRoadBits(tile) & ((Axis)p1 == AXIS_X ? ROAD_Y : ROAD_X)) != 0) return_cmd_error(STR_DRIVE_THROUGH_ERROR_DIRECTION);
01423
01424 if (!CheckIfAuthorityAllowsNewStation(tile, flags)) return CMD_ERROR;
01425
01426 RoadTypes cur_rts = IsNormalRoadTile(tile) ? GetRoadTypes(tile) : ROADTYPES_NONE;
01427 uint num_roadbits = 0;
01428
01429 if (build_over_road) {
01430
01431 if (HasBit(cur_rts, ROADTYPE_ROAD)) {
01432 road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
01433 if (road_owner == OWNER_TOWN) {
01434 if (!_settings_game.construction.road_stop_on_town_road) return_cmd_error(STR_DRIVE_THROUGH_ERROR_ON_TOWN_ROAD);
01435 } else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE && !CheckOwnership(road_owner)) {
01436 return CMD_ERROR;
01437 }
01438 num_roadbits += CountBits(GetRoadBits(tile, ROADTYPE_ROAD));
01439 }
01440
01441
01442 if (HasBit(cur_rts, ROADTYPE_TRAM)) {
01443 tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
01444 if (!_settings_game.construction.road_stop_on_competitor_road && tram_owner != OWNER_NONE && !CheckOwnership(tram_owner)) {
01445 return CMD_ERROR;
01446 }
01447 num_roadbits += CountBits(GetRoadBits(tile, ROADTYPE_TRAM));
01448 }
01449
01450
01451 if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
01452
01453
01454 rts |= cur_rts;
01455 }
01456
01457 CommandCost cost = CheckFlatLandBelow(tile, 1, 1, flags, is_drive_through ? 5 << p1 : 1 << p1, NULL, !build_over_road);
01458 if (CmdFailed(cost)) return cost;
01459 uint roadbits_to_build = CountBits(rts) * 2 - num_roadbits;
01460 cost.AddCost(_price.build_road * roadbits_to_build);
01461
01462 Station *st = NULL;
01463
01464 if (!_settings_game.station.adjacent_stations || !HasBit(p2, 5)) {
01465 st = GetStationAround(tile, 1, 1, INVALID_STATION);
01466 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
01467 }
01468
01469
01470 if (st == NULL && distant_join) st = GetStation(station_to_join);
01471
01472
01473 if (st == NULL) st = GetClosestDeletedStation(tile);
01474
01475
01476 if (!RoadStop::CanAllocateItem()) return_cmd_error(type ? STR_TOO_MANY_TRUCK_STOPS : STR_TOO_MANY_BUS_STOPS);
01477
01478 if (st != NULL &&
01479 GetNumRoadStopsInStation(st, ROADSTOP_BUS) + GetNumRoadStopsInStation(st, ROADSTOP_TRUCK) >= RoadStop::LIMIT) {
01480 return_cmd_error(type ? STR_TOO_MANY_TRUCK_STOPS : STR_TOO_MANY_BUS_STOPS);
01481 }
01482
01483 if (st != NULL) {
01484 if (st->owner != _current_company) {
01485 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
01486 }
01487
01488 if (!st->rect.BeforeAddTile(tile, StationRect::ADD_TEST)) return CMD_ERROR;
01489 } else {
01490
01491 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
01492
01493 if (flags & DC_EXEC) {
01494 st = new Station(tile);
01495
01496 st->town = ClosestTownFromTile(tile, UINT_MAX);
01497 st->string_id = GenerateStationName(st, tile, STATIONNAMING_ROAD);
01498
01499 if (IsValidCompanyID(_current_company)) {
01500 SetBit(st->town->have_ratings, _current_company);
01501 }
01502 st->sign.width_1 = 0;
01503 }
01504 }
01505
01506 cost.AddCost((type) ? _price.build_truck_station : _price.build_bus_station);
01507
01508 if (flags & DC_EXEC) {
01509 RoadStop *road_stop = new RoadStop(tile);
01510
01511 RoadStop **currstop = FindRoadStopSpot(type, st);
01512 *currstop = road_stop;
01513
01514
01515 st->AddFacility((type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP, tile);
01516
01517 st->rect.BeforeAddTile(tile, StationRect::ADD_TRY);
01518
01519 RoadStopType rs_type = type ? ROADSTOP_TRUCK : ROADSTOP_BUS;
01520 if (is_drive_through) {
01521 MakeDriveThroughRoadStop(tile, st->owner, road_owner, tram_owner, st->index, rs_type, rts, (Axis)p1);
01522 } else {
01523 MakeRoadStop(tile, st->owner, st->index, rs_type, rts, (DiagDirection)p1);
01524 }
01525
01526 UpdateStationVirtCoordDirty(st);
01527 UpdateStationAcceptance(st, false);
01528 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
01529 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
01530 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_ROADVEHS);
01531 }
01532 return cost;
01533 }
01534
01535
01536 static Vehicle *ClearRoadStopStatusEnum(Vehicle *v, void *)
01537 {
01538 if (v->type == VEH_ROAD) ClrBit(v->u.road.state, RVS_IN_DT_ROAD_STOP);
01539
01540 return NULL;
01541 }
01542
01543
01550 static CommandCost RemoveRoadStop(Station *st, DoCommandFlag flags, TileIndex tile)
01551 {
01552 if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) {
01553 return CMD_ERROR;
01554 }
01555
01556 bool is_truck = IsTruckStop(tile);
01557
01558 RoadStop **primary_stop;
01559 RoadStop *cur_stop;
01560 if (is_truck) {
01561 primary_stop = &st->truck_stops;
01562 cur_stop = GetRoadStopByTile(tile, ROADSTOP_TRUCK);
01563 } else {
01564 primary_stop = &st->bus_stops;
01565 cur_stop = GetRoadStopByTile(tile, ROADSTOP_BUS);
01566 }
01567
01568 assert(cur_stop != NULL);
01569
01570
01571 if (IsDriveThroughStopTile(tile) && (flags & DC_BANKRUPT)) {
01572
01573 if (flags & DC_EXEC) FindVehicleOnPos(tile, NULL, &ClearRoadStopStatusEnum);
01574 } else {
01575 if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
01576 }
01577
01578 if (flags & DC_EXEC) {
01579 if (*primary_stop == cur_stop) {
01580
01581 *primary_stop = cur_stop->next;
01582
01583 if (*primary_stop == NULL) {
01584 st->facilities &= (is_truck ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP);
01585 }
01586 } else {
01587
01588 RoadStop *pred = *primary_stop;
01589 while (pred->next != cur_stop) pred = pred->next;
01590 pred->next = cur_stop->next;
01591 }
01592
01593 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_ROADVEHS);
01594 delete cur_stop;
01595
01596
01597 Vehicle *v;
01598 FOR_ALL_VEHICLES(v) {
01599 if (v->type == VEH_ROAD &&
01600 v->First() == v &&
01601 v->current_order.IsType(OT_GOTO_STATION) &&
01602 v->dest_tile == tile) {
01603 v->dest_tile = v->GetOrderStationLocation(st->index);
01604 }
01605 }
01606
01607 DoClearSquare(tile);
01608 st->rect.AfterRemoveTile(st, tile);
01609
01610 UpdateStationVirtCoordDirty(st);
01611 DeleteStationIfEmpty(st);
01612 }
01613
01614 return CommandCost(EXPENSES_CONSTRUCTION, (is_truck) ? _price.remove_truck_station : _price.remove_bus_station);
01615 }
01616
01623 CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01624 {
01625
01626 if (!IsTileType(tile, MP_STATION) || !IsRoadStop(tile) || (uint32)GetRoadStopType(tile) != GB(p2, 0, 1)) return CMD_ERROR;
01627 Station *st = GetStationByTile(tile);
01628
01629 bool is_drive_through = IsDriveThroughStopTile(tile);
01630 RoadTypes rts = GetRoadTypes(tile);
01631 RoadBits road_bits = IsDriveThroughStopTile(tile) ?
01632 ((GetRoadStopDir(tile) == DIAGDIR_NE) ? ROAD_X : ROAD_Y) :
01633 DiagDirToRoadBits(GetRoadStopDir(tile));
01634
01635 CommandCost ret = RemoveRoadStop(st, flags, tile);
01636
01637
01638 if ((flags & DC_EXEC) && CmdSucceeded(ret) && is_drive_through) {
01639
01640
01641
01642 MakeRoadNormal(tile, road_bits, rts, ClosestTownFromTile(tile, UINT_MAX)->index,
01643 GetRoadOwner(tile, ROADTYPE_ROAD), GetRoadOwner(tile, ROADTYPE_TRAM));
01644 }
01645
01646 return ret;
01647 }
01648
01649
01650
01651
01652 static const byte _airport_sections_country[] = {
01653 54, 53, 52, 65,
01654 58, 57, 56, 55,
01655 64, 63, 63, 62
01656 };
01657
01658
01659 static const byte _airport_sections_town[] = {
01660 31, 9, 33, 9, 9, 32,
01661 27, 36, 29, 34, 8, 10,
01662 30, 11, 35, 13, 20, 21,
01663 51, 12, 14, 17, 19, 28,
01664 38, 13, 15, 16, 18, 39,
01665 26, 22, 23, 24, 25, 26
01666 };
01667
01668
01669 static const byte _airport_sections_metropolitan[] = {
01670 31, 9, 33, 9, 9, 32,
01671 27, 36, 29, 34, 8, 10,
01672 30, 11, 35, 13, 20, 21,
01673 102, 8, 8, 8, 8, 28,
01674 83, 84, 84, 84, 84, 83,
01675 26, 23, 23, 23, 23, 26
01676 };
01677
01678
01679 static const byte _airport_sections_international[] = {
01680 88, 89, 89, 89, 89, 89, 88,
01681 51, 8, 8, 8, 8, 8, 32,
01682 30, 8, 11, 27, 11, 8, 10,
01683 32, 8, 11, 27, 11, 8, 114,
01684 87, 8, 11, 85, 11, 8, 114,
01685 87, 8, 8, 8, 8, 8, 90,
01686 26, 23, 23, 23, 23, 23, 26
01687 };
01688
01689
01690 static const byte _airport_sections_intercontinental[] = {
01691 102, 120, 89, 89, 89, 89, 89, 89, 118,
01692 120, 23, 23, 23, 23, 23, 23, 119, 117,
01693 87, 54, 87, 8, 8, 8, 8, 51, 117,
01694 87, 162, 87, 85, 116, 116, 8, 9, 10,
01695 87, 8, 8, 11, 31, 11, 8, 160, 32,
01696 32, 160, 8, 11, 27, 11, 8, 8, 10,
01697 87, 8, 8, 11, 30, 11, 8, 8, 10,
01698 87, 142, 8, 11, 29, 11, 10, 163, 10,
01699 87, 164, 87, 8, 8, 8, 10, 37, 117,
01700 87, 120, 89, 89, 89, 89, 89, 89, 119,
01701 121, 23, 23, 23, 23, 23, 23, 119, 37
01702 };
01703
01704
01705
01706 static const byte _airport_sections_commuter[] = {
01707 85, 30, 115, 115, 32,
01708 87, 8, 8, 8, 10,
01709 87, 11, 11, 11, 10,
01710 26, 23, 23, 23, 26
01711 };
01712
01713
01714 static const byte _airport_sections_heliport[] = {
01715 66,
01716 };
01717
01718
01719 static const byte _airport_sections_helidepot[] = {
01720 124, 32,
01721 122, 123
01722 };
01723
01724
01725 static const byte _airport_sections_helistation[] = {
01726 32, 134, 159, 158,
01727 161, 142, 142, 157
01728 };
01729
01730 static const byte * const _airport_sections[] = {
01731 _airport_sections_country,
01732 _airport_sections_town,
01733 _airport_sections_heliport,
01734 _airport_sections_metropolitan,
01735 _airport_sections_international,
01736 _airport_sections_commuter,
01737 _airport_sections_helidepot,
01738 _airport_sections_intercontinental,
01739 _airport_sections_helistation
01740 };
01741
01749 static uint GetMinimalAirportDistanceToTile(const AirportFTAClass *afc, TileIndex town_tile, TileIndex airport_tile)
01750 {
01751 uint ttx = TileX(town_tile);
01752 uint tty = TileY(town_tile);
01753
01754 uint atx = TileX(airport_tile);
01755 uint aty = TileY(airport_tile);
01756
01757 uint btx = TileX(airport_tile) + afc->size_x - 1;
01758 uint bty = TileY(airport_tile) + afc->size_y - 1;
01759
01760
01761
01762
01763 uint dx = ttx < atx ? atx - ttx : (ttx <= btx ? 0 : ttx - btx);
01764 uint dy = tty < aty ? aty - tty : (tty <= bty ? 0 : tty - bty);
01765
01766 return dx + dy;
01767 }
01768
01777 uint8 GetAirportNoiseLevelForTown(const AirportFTAClass *afc, TileIndex town_tile, TileIndex tile)
01778 {
01779
01780
01781 if (afc->noise_level < 2) return afc->noise_level;
01782
01783 uint distance = GetMinimalAirportDistanceToTile(afc, town_tile, tile);
01784
01785
01786
01787
01788
01789 uint8 town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
01790
01791
01792
01793 uint noise_reduction = distance / town_tolerance_distance;
01794
01795
01796
01797 return noise_reduction >= afc->noise_level ? 1 : afc->noise_level - noise_reduction;
01798 }
01799
01807 Town *AirportGetNearestTown(const AirportFTAClass *afc, TileIndex airport_tile)
01808 {
01809 Town *t, *nearest = NULL;
01810 uint add = afc->size_x + afc->size_y - 2;
01811 uint mindist = UINT_MAX - add;
01812 FOR_ALL_TOWNS(t) {
01813 if (DistanceManhattan(t->xy, airport_tile) < mindist + add) {
01814 uint dist = GetMinimalAirportDistanceToTile(afc, t->xy, airport_tile);
01815 if (dist < mindist) {
01816 nearest = t;
01817 mindist = dist;
01818 }
01819 }
01820 }
01821
01822 return nearest;
01823 }
01824
01825
01827 void UpdateAirportsNoise()
01828 {
01829 Town *t;
01830 const Station *st;
01831
01832 FOR_ALL_TOWNS(t) t->noise_reached = 0;
01833
01834 FOR_ALL_STATIONS(st) {
01835 if (st->airport_tile != INVALID_TILE) {
01836 const AirportFTAClass *afc = GetAirport(st->airport_type);
01837 Town *nearest = AirportGetNearestTown(afc, st->airport_tile);
01838 nearest->noise_reached += GetAirportNoiseLevelForTown(afc, nearest->xy, st->airport_tile);
01839 }
01840 }
01841 }
01842
01843
01852 CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01853 {
01854 bool airport_upgrade = true;
01855 StationID station_to_join = GB(p2, 16, 16);
01856 bool distant_join = (station_to_join != INVALID_STATION);
01857
01858 if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR;
01859
01860
01861 if (p1 > lengthof(_airport_sections) || !HasBit(GetValidAirports(), p1)) return CMD_ERROR;
01862
01863 if (!CheckIfAuthorityAllowsNewStation(tile, flags)) {
01864 return CMD_ERROR;
01865 }
01866
01867 Town *t = ClosestTownFromTile(tile, UINT_MAX);
01868 const AirportFTAClass *afc = GetAirport(p1);
01869 int w = afc->size_x;
01870 int h = afc->size_y;
01871 Station *st = NULL;
01872
01873 if (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread) {
01874 _error_message = STR_306C_STATION_TOO_SPREAD_OUT;
01875 return CMD_ERROR;
01876 }
01877
01878 CommandCost cost = CheckFlatLandBelow(tile, w, h, flags, 0, NULL);
01879 if (CmdFailed(cost)) return cost;
01880
01881
01882 Town *nearest = AirportGetNearestTown(afc, tile);
01883 uint newnoise_level = GetAirportNoiseLevelForTown(afc, nearest->xy, tile);
01884
01885
01886 StringID authority_refuse_message = STR_NULL;
01887
01888 if (_settings_game.economy.station_noise_level) {
01889
01890 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
01891 authority_refuse_message = STR_LOCAL_AUTHORITY_REFUSES_NOISE;
01892 }
01893 } else {
01894 uint num = 0;
01895 const Station *st;
01896 FOR_ALL_STATIONS(st) {
01897 if (st->town == t && st->facilities & FACIL_AIRPORT && st->airport_type != AT_OILRIG) num++;
01898 }
01899 if (num >= 2) {
01900 authority_refuse_message = STR_2035_LOCAL_AUTHORITY_REFUSES;
01901 }
01902 }
01903
01904 if (authority_refuse_message != STR_NULL) {
01905 SetDParam(0, t->index);
01906 return_cmd_error(authority_refuse_message);
01907 }
01908
01909 if (!_settings_game.station.adjacent_stations || !HasBit(p2, 0)) {
01910 st = GetStationAround(tile, w, h, INVALID_STATION);
01911 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
01912 } else {
01913 st = NULL;
01914 }
01915
01916
01917 if (st == NULL && distant_join) st = GetStation(station_to_join);
01918
01919
01920 if (st == NULL) st = GetClosestDeletedStation(tile);
01921
01922 if (st != NULL) {
01923 if (st->owner != _current_company) {
01924 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
01925 }
01926
01927 if (!st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TEST)) return CMD_ERROR;
01928
01929 if (st->airport_tile != INVALID_TILE) {
01930 return_cmd_error(STR_300D_TOO_CLOSE_TO_ANOTHER_AIRPORT);
01931 }
01932 } else {
01933 airport_upgrade = false;
01934
01935
01936 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
01937
01938 if (flags & DC_EXEC) {
01939 st = new Station(tile);
01940
01941 st->town = t;
01942 st->string_id = GenerateStationName(st, tile, !(afc->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_HELIPORT : STATIONNAMING_AIRPORT);
01943
01944 if (IsValidCompanyID(_current_company)) {
01945 SetBit(st->town->have_ratings, _current_company);
01946 }
01947 st->sign.width_1 = 0;
01948 }
01949 }
01950
01951 cost.AddCost(_price.build_airport * w * h);
01952
01953 if (flags & DC_EXEC) {
01954
01955 nearest->noise_reached += newnoise_level;
01956
01957 st->airport_tile = tile;
01958 st->AddFacility(FACIL_AIRPORT, tile);
01959 st->airport_type = (byte)p1;
01960 st->airport_flags = 0;
01961
01962 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
01963
01964
01965
01966
01967
01968
01969
01970
01971 if (airport_upgrade) UpdateAirplanesOnNewStation(st);
01972
01973 {
01974 const byte *b = _airport_sections[p1];
01975
01976 BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
01977 MakeAirport(tile_cur, st->owner, st->index, *b - ((*b < 67) ? 8 : 24));
01978 b++;
01979 } END_TILE_LOOP(tile_cur, w, h, tile)
01980 }
01981
01982 UpdateStationVirtCoordDirty(st);
01983 UpdateStationAcceptance(st, false);
01984 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
01985 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
01986 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_PLANES);
01987
01988 if (_settings_game.economy.station_noise_level) {
01989 InvalidateWindow(WC_TOWN_VIEW, st->town->index);
01990 }
01991 }
01992
01993 return cost;
01994 }
01995
01996 static CommandCost RemoveAirport(Station *st, DoCommandFlag flags)
01997 {
01998 if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) {
01999 return CMD_ERROR;
02000 }
02001
02002 TileIndex tile = st->airport_tile;
02003
02004 const AirportFTAClass *afc = st->Airport();
02005 int w = afc->size_x;
02006 int h = afc->size_y;
02007
02008 CommandCost cost(EXPENSES_CONSTRUCTION, w * h * _price.remove_airport);
02009
02010 const Vehicle *v;
02011 FOR_ALL_VEHICLES(v) {
02012 if (!(v->type == VEH_AIRCRAFT && IsNormalAircraft(v))) continue;
02013
02014 if (v->u.air.targetairport == st->index && v->u.air.state != FLYING) return CMD_ERROR;
02015 }
02016
02017 BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
02018 if (!EnsureNoVehicleOnGround(tile_cur)) return CMD_ERROR;
02019
02020 if (flags & DC_EXEC) {
02021 DeleteAnimatedTile(tile_cur);
02022 DoClearSquare(tile_cur);
02023 }
02024 } END_TILE_LOOP(tile_cur, w, h, tile)
02025
02026 if (flags & DC_EXEC) {
02027 for (uint i = 0; i < afc->nof_depots; ++i) {
02028 DeleteWindowById(
02029 WC_VEHICLE_DEPOT, tile + ToTileIndexDiff(afc->airport_depots[i])
02030 );
02031 }
02032
02033
02034
02035
02036 Town *nearest = AirportGetNearestTown(afc, tile);
02037 nearest->noise_reached -= GetAirportNoiseLevelForTown(afc, nearest->xy, tile);
02038
02039 st->rect.AfterRemoveRect(st, tile, w, h);
02040
02041 st->airport_tile = INVALID_TILE;
02042 st->facilities &= ~FACIL_AIRPORT;
02043
02044 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_PLANES);
02045
02046 if (_settings_game.economy.station_noise_level) {
02047 InvalidateWindow(WC_TOWN_VIEW, st->town->index);
02048 }
02049
02050 UpdateStationVirtCoordDirty(st);
02051 DeleteStationIfEmpty(st);
02052 }
02053
02054 return cost;
02055 }
02056
02063 CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02064 {
02065 if (!IsWaterTile(tile) || tile == 0) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02066 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
02067
02068 if (GetTileSlope(tile, NULL) != SLOPE_FLAT) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02069
02070
02071 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
02072
02073 if (flags & DC_EXEC) {
02074 Station *st = new Station(tile);
02075
02076 st->town = ClosestTownFromTile(tile, UINT_MAX);
02077 st->string_id = GenerateStationName(st, tile, STATIONNAMING_BUOY);
02078
02079 if (IsValidCompanyID(_current_company)) {
02080 SetBit(st->town->have_ratings, _current_company);
02081 }
02082 st->sign.width_1 = 0;
02083 st->dock_tile = tile;
02084 st->facilities |= FACIL_DOCK;
02085
02086
02087 st->had_vehicle_of_type |= HVOT_BUOY;
02088 st->owner = OWNER_NONE;
02089
02090 st->build_date = _date;
02091
02092 MakeBuoy(tile, st->index, GetWaterClass(tile));
02093
02094 UpdateStationVirtCoordDirty(st);
02095 UpdateStationAcceptance(st, false);
02096 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
02097 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02098 }
02099
02100 return CommandCost(EXPENSES_CONSTRUCTION, _price.build_dock);
02101 }
02102
02109 bool HasStationInUse(StationID station, CompanyID company)
02110 {
02111 const Vehicle *v;
02112 FOR_ALL_VEHICLES(v) {
02113 if (company == INVALID_COMPANY || v->owner == company) {
02114 const Order *order;
02115 FOR_VEHICLE_ORDERS(v, order) {
02116 if (order->IsType(OT_GOTO_STATION) && order->GetDestination() == station) {
02117 return true;
02118 }
02119 }
02120 }
02121 }
02122 return false;
02123 }
02124
02125 static CommandCost RemoveBuoy(Station *st, DoCommandFlag flags)
02126 {
02127
02128 if (!IsValidCompanyID(_current_company)) return_cmd_error(INVALID_STRING_ID);
02129
02130 TileIndex tile = st->dock_tile;
02131
02132 if (HasStationInUse(st->index, INVALID_COMPANY)) return_cmd_error(STR_BUOY_IS_IN_USE);
02133
02134 if (!(flags & DC_BANKRUPT) && !EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
02135
02136 if (flags & DC_EXEC) {
02137 st->dock_tile = INVALID_TILE;
02138
02139
02140 st->facilities &= ~FACIL_DOCK;
02141 st->had_vehicle_of_type &= ~HVOT_BUOY;
02142
02143 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02144
02145
02146
02147
02148 MakeWaterKeepingClass(tile, GetTileOwner(tile));
02149 MarkTileDirtyByTile(tile);
02150
02151 UpdateStationVirtCoordDirty(st);
02152 DeleteStationIfEmpty(st);
02153 }
02154
02155 return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_truck_station);
02156 }
02157
02158 static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
02159 {-1, 0},
02160 { 0, 0},
02161 { 0, 0},
02162 { 0, -1}
02163 };
02164 static const byte _dock_w_chk[4] = { 2, 1, 2, 1 };
02165 static const byte _dock_h_chk[4] = { 1, 2, 1, 2 };
02166
02173 CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02174 {
02175 StationID station_to_join = GB(p2, 16, 16);
02176 bool distant_join = (station_to_join != INVALID_STATION);
02177
02178 if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR;
02179
02180 DiagDirection direction = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
02181 if (direction == INVALID_DIAGDIR) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02182 direction = ReverseDiagDir(direction);
02183
02184
02185 if (IsWaterTile(tile)) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02186
02187 if (!CheckIfAuthorityAllowsNewStation(tile, flags)) return CMD_ERROR;
02188
02189 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
02190
02191 if (CmdFailed(DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR))) return CMD_ERROR;
02192
02193 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
02194
02195 if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
02196 return_cmd_error(STR_304B_SITE_UNSUITABLE);
02197 }
02198
02199 if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
02200
02201
02202 WaterClass wc = GetWaterClass(tile_cur);
02203
02204 if (CmdFailed(DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR))) return CMD_ERROR;
02205
02206 tile_cur += TileOffsByDiagDir(direction);
02207 if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
02208 return_cmd_error(STR_304B_SITE_UNSUITABLE);
02209 }
02210
02211
02212 Station *st = NULL;
02213
02214 if (!_settings_game.station.adjacent_stations || !HasBit(p1, 0)) {
02215 st = GetStationAround(
02216 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02217 _dock_w_chk[direction], _dock_h_chk[direction], INVALID_STATION);
02218 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
02219 }
02220
02221
02222 if (st == NULL && distant_join) st = GetStation(station_to_join);
02223
02224
02225 if (st == NULL) st = GetClosestDeletedStation(tile);
02226
02227 if (st != NULL) {
02228 if (st->owner != _current_company) {
02229 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
02230 }
02231
02232 if (!st->rect.BeforeAddRect(
02233 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02234 _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TEST)) return CMD_ERROR;
02235
02236 if (st->dock_tile != INVALID_TILE) return_cmd_error(STR_304C_TOO_CLOSE_TO_ANOTHER_DOCK);
02237 } else {
02238
02239 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
02240
02241 if (flags & DC_EXEC) {
02242 st = new Station(tile);
02243
02244 st->town = ClosestTownFromTile(tile, UINT_MAX);
02245 st->string_id = GenerateStationName(st, tile, STATIONNAMING_DOCK);
02246
02247 if (IsValidCompanyID(_current_company)) {
02248 SetBit(st->town->have_ratings, _current_company);
02249 }
02250 }
02251 }
02252
02253 if (flags & DC_EXEC) {
02254 st->dock_tile = tile;
02255 st->AddFacility(FACIL_DOCK, tile);
02256
02257 st->rect.BeforeAddRect(
02258 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02259 _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TRY);
02260
02261 MakeDock(tile, st->owner, st->index, direction, wc);
02262
02263 UpdateStationVirtCoordDirty(st);
02264 UpdateStationAcceptance(st, false);
02265 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
02266 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
02267 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02268 }
02269
02270 return CommandCost(EXPENSES_CONSTRUCTION, _price.build_dock);
02271 }
02272
02273 static CommandCost RemoveDock(Station *st, DoCommandFlag flags)
02274 {
02275 if (!CheckOwnership(st->owner)) return CMD_ERROR;
02276
02277 TileIndex tile1 = st->dock_tile;
02278 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
02279
02280 if (!EnsureNoVehicleOnGround(tile1)) return CMD_ERROR;
02281 if (!EnsureNoVehicleOnGround(tile2)) return CMD_ERROR;
02282
02283 if (flags & DC_EXEC) {
02284 DoClearSquare(tile1);
02285 MakeWaterKeepingClass(tile2, st->owner);
02286
02287 st->rect.AfterRemoveTile(st, tile1);
02288 st->rect.AfterRemoveTile(st, tile2);
02289
02290 MarkTileDirtyByTile(tile2);
02291
02292 st->dock_tile = INVALID_TILE;
02293 st->facilities &= ~FACIL_DOCK;
02294
02295 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02296 UpdateStationVirtCoordDirty(st);
02297 DeleteStationIfEmpty(st);
02298 }
02299
02300 return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_dock);
02301 }
02302
02303 #include "table/station_land.h"
02304
02305 const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx)
02306 {
02307 return &_station_display_datas[st][gfx];
02308 }
02309
02310 static void DrawTile_Station(TileInfo *ti)
02311 {
02312 const DrawTileSprites *t = NULL;
02313 RoadTypes roadtypes;
02314 int32 total_offset;
02315 int32 custom_ground_offset;
02316
02317 if (IsRailwayStation(ti->tile)) {
02318 const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
02319 roadtypes = ROADTYPES_NONE;
02320 total_offset = rti->total_offset;
02321 custom_ground_offset = rti->custom_ground_offset;
02322 } else {
02323 roadtypes = IsRoadStop(ti->tile) ? GetRoadTypes(ti->tile) : ROADTYPES_NONE;
02324 total_offset = 0;
02325 custom_ground_offset = 0;
02326 }
02327 uint32 relocation = 0;
02328 const Station *st = NULL;
02329 const StationSpec *statspec = NULL;
02330 Owner owner = GetTileOwner(ti->tile);
02331
02332 SpriteID palette;
02333 if (IsValidCompanyID(owner)) {
02334 palette = COMPANY_SPRITE_COLOUR(owner);
02335 } else {
02336
02337 palette = PALETTE_TO_GREY;
02338 }
02339
02340
02341 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile))
02342 DrawFoundation(ti, FOUNDATION_LEVELED);
02343
02344 if (IsCustomStationSpecIndex(ti->tile)) {
02345
02346 st = GetStationByTile(ti->tile);
02347 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
02348
02349 if (statspec != NULL) {
02350 uint tile = GetStationGfx(ti->tile);
02351
02352 relocation = GetCustomStationRelocation(statspec, st, ti->tile);
02353
02354 if (HasBit(statspec->callbackmask, CBM_STATION_SPRITE_LAYOUT)) {
02355 uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile);
02356 if (callback != CALLBACK_FAILED) tile = (callback & ~1) + GetRailStationAxis(ti->tile);
02357 }
02358
02359
02360 if (statspec->renderdata != NULL) {
02361 t = &statspec->renderdata[tile < statspec->tiles ? tile : (uint)GetRailStationAxis(ti->tile)];
02362 }
02363 }
02364 }
02365
02366 if (t == NULL || t->seq == NULL) t = &_station_display_datas[GetStationType(ti->tile)][GetStationGfx(ti->tile)];
02367
02368
02369 if (IsBuoy(ti->tile) || IsDock(ti->tile) || (IsOilRig(ti->tile) && GetWaterClass(ti->tile) != WATER_CLASS_INVALID)) {
02370 if (ti->tileh == SLOPE_FLAT) {
02371 DrawWaterClassGround(ti);
02372 } else {
02373 assert(IsDock(ti->tile));
02374 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
02375 WaterClass wc = GetWaterClass(water_tile);
02376 if (wc == WATER_CLASS_SEA) {
02377 DrawShoreTile(ti->tileh);
02378 } else {
02379 DrawClearLandTile(ti, 3);
02380 }
02381 }
02382 } else {
02383 SpriteID image = t->ground.sprite;
02384 SpriteID pal = t->ground.pal;
02385 if (HasBit(image, SPRITE_MODIFIER_USE_OFFSET)) {
02386 image += GetCustomStationGroundRelocation(statspec, st, ti->tile);
02387 image += custom_ground_offset;
02388 } else {
02389 image += total_offset;
02390 }
02391 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
02392
02393
02394 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && IsRailwayStation(ti->tile) && GetRailwayStationReservation(ti->tile)) {
02395 const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
02396 DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH);
02397 }
02398 }
02399
02400 if (IsRailwayStation(ti->tile) && HasCatenaryDrawn(GetRailType(ti->tile)) && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
02401
02402 if (HasBit(roadtypes, ROADTYPE_TRAM)) {
02403 Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
02404 DrawGroundSprite((HasBit(roadtypes, ROADTYPE_ROAD) ? SPR_TRAMWAY_OVERLAY : SPR_TRAMWAY_TRAM) + (axis ^ 1), PAL_NONE);
02405 DrawTramCatenary(ti, axis == AXIS_X ? ROAD_X : ROAD_Y);
02406 }
02407
02408 const DrawTileSeqStruct *dtss;
02409 foreach_draw_tile_seq(dtss, t->seq) {
02410 SpriteID image = dtss->image.sprite;
02411
02412
02413 if (IsInvisibilitySet(TO_BUILDINGS) && !HasBit(image, SPRITE_MODIFIER_OPAQUE)) return;
02414
02415 if (relocation == 0 || HasBit(image, SPRITE_MODIFIER_USE_OFFSET)) {
02416 image += total_offset;
02417 } else {
02418 image += relocation;
02419 }
02420
02421 SpriteID pal = SpriteLayoutPaletteTransform(image, dtss->image.pal, palette);
02422
02423 if ((byte)dtss->delta_z != 0x80) {
02424 AddSortableSpriteToDraw(
02425 image, pal,
02426 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
02427 dtss->size_x, dtss->size_y,
02428 dtss->size_z, ti->z + dtss->delta_z,
02429 !HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_BUILDINGS)
02430 );
02431 } else {
02432
02433 AddChildSpriteScreen(image, pal, dtss->delta_x, dtss->delta_y, !HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_BUILDINGS));
02434 }
02435 }
02436 }
02437
02438 void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
02439 {
02440 int32 total_offset = 0;
02441 SpriteID pal = COMPANY_SPRITE_COLOUR(_local_company);
02442 const DrawTileSprites *t = &_station_display_datas[st][image];
02443
02444 if (railtype != INVALID_RAILTYPE) {
02445 const RailtypeInfo *rti = GetRailTypeInfo(railtype);
02446 total_offset = rti->total_offset;
02447 }
02448
02449 SpriteID img = t->ground.sprite;
02450 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
02451
02452 if (roadtype == ROADTYPE_TRAM) {
02453 DrawSprite(SPR_TRAMWAY_TRAM + (t->ground.sprite == SPR_ROAD_PAVED_STRAIGHT_X ? 1 : 0), PAL_NONE, x, y);
02454 }
02455
02456 const DrawTileSeqStruct *dtss;
02457 foreach_draw_tile_seq(dtss, t->seq) {
02458 Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z);
02459 DrawSprite(dtss->image.sprite + total_offset, pal, x + pt.x, y + pt.y);
02460 }
02461 }
02462
02463 static uint GetSlopeZ_Station(TileIndex tile, uint x, uint y)
02464 {
02465 return GetTileMaxZ(tile);
02466 }
02467
02468 static Foundation GetFoundation_Station(TileIndex tile, Slope tileh)
02469 {
02470 return FlatteningFoundation(tileh);
02471 }
02472
02473 static void GetAcceptedCargo_Station(TileIndex tile, AcceptedCargo ac)
02474 {
02475
02476 }
02477
02478 static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
02479 {
02480 td->owner[0] = GetTileOwner(tile);
02481 if (IsDriveThroughStopTile(tile)) {
02482 Owner road_owner = INVALID_OWNER;
02483 Owner tram_owner = INVALID_OWNER;
02484 RoadTypes rts = GetRoadTypes(tile);
02485 if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
02486 if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
02487
02488
02489 if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) ||
02490 (road_owner != INVALID_OWNER && road_owner != td->owner[0])) {
02491 uint i = 1;
02492 if (road_owner != INVALID_OWNER) {
02493 td->owner_type[i] = STR_ROAD_OWNER;
02494 td->owner[i] = road_owner;
02495 i++;
02496 }
02497 if (tram_owner != INVALID_OWNER) {
02498 td->owner_type[i] = STR_TRAM_OWNER;
02499 td->owner[i] = tram_owner;
02500 }
02501 }
02502 }
02503 td->build_date = GetStationByTile(tile)->build_date;
02504
02505 const StationSpec *spec = GetStationSpec(tile);
02506
02507 if (spec != NULL) {
02508 td->station_class = GetStationClassName(spec->sclass);
02509 td->station_name = spec->name;
02510
02511 if (spec->grffile != NULL) {
02512 const GRFConfig *gc = GetGRFConfig(spec->grffile->grfid);
02513 td->grf = gc->name;
02514 }
02515 }
02516
02517 StringID str;
02518 switch (GetStationType(tile)) {
02519 default: NOT_REACHED();
02520 case STATION_RAIL: str = STR_305E_RAILROAD_STATION; break;
02521 case STATION_AIRPORT:
02522 str = (IsHangar(tile) ? STR_305F_AIRCRAFT_HANGAR : STR_3060_AIRPORT);
02523 break;
02524 case STATION_TRUCK: str = STR_3061_TRUCK_LOADING_AREA; break;
02525 case STATION_BUS: str = STR_3062_BUS_STATION; break;
02526 case STATION_OILRIG: str = STR_4807_OIL_RIG; break;
02527 case STATION_DOCK: str = STR_3063_SHIP_DOCK; break;
02528 case STATION_BUOY: str = STR_3069_BUOY; break;
02529 }
02530 td->str = str;
02531 }
02532
02533
02534 static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
02535 {
02536 TrackBits trackbits = TRACK_BIT_NONE;
02537
02538 switch (mode) {
02539 case TRANSPORT_RAIL:
02540 if (IsRailwayStation(tile) && !IsStationTileBlocked(tile)) {
02541 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
02542 }
02543 break;
02544
02545 case TRANSPORT_WATER:
02546
02547 if (IsBuoy(tile)) {
02548 trackbits = TRACK_BIT_ALL;
02549
02550 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
02551
02552 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
02553 }
02554 break;
02555
02556 case TRANSPORT_ROAD:
02557 if ((GetRoadTypes(tile) & sub_mode) != 0 && IsRoadStop(tile)) {
02558 DiagDirection dir = GetRoadStopDir(tile);
02559 Axis axis = DiagDirToAxis(dir);
02560
02561 if (side != INVALID_DIAGDIR) {
02562 if (axis != DiagDirToAxis(side) || (IsStandardRoadStopTile(tile) && dir != side)) break;
02563 }
02564
02565 trackbits = AxisToTrackBits(axis);
02566 }
02567 break;
02568
02569 default:
02570 break;
02571 }
02572
02573 return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), TRACKDIR_BIT_NONE);
02574 }
02575
02576
02577 static void TileLoop_Station(TileIndex tile)
02578 {
02579
02580
02581 switch (GetStationType(tile)) {
02582 case STATION_AIRPORT:
02583 switch (GetStationGfx(tile)) {
02584 case GFX_RADAR_LARGE_FIRST:
02585 case GFX_WINDSACK_FIRST :
02586 case GFX_RADAR_INTERNATIONAL_FIRST:
02587 case GFX_RADAR_METROPOLITAN_FIRST:
02588 case GFX_RADAR_DISTRICTWE_FIRST:
02589 case GFX_WINDSACK_INTERCON_FIRST :
02590 AddAnimatedTile(tile);
02591 break;
02592 }
02593 break;
02594
02595 case STATION_DOCK:
02596 if (GetTileSlope(tile, NULL) != SLOPE_FLAT) break;
02597
02598 case STATION_OILRIG:
02599 case STATION_BUOY:
02600 TileLoop_Water(tile);
02601 break;
02602
02603 default: break;
02604 }
02605 }
02606
02607
02608 static void AnimateTile_Station(TileIndex tile)
02609 {
02610 struct AnimData {
02611 StationGfx from;
02612 StationGfx to;
02613 byte delay;
02614 };
02615
02616 static const AnimData data[] = {
02617 { GFX_RADAR_LARGE_FIRST, GFX_RADAR_LARGE_LAST, 3 },
02618 { GFX_WINDSACK_FIRST, GFX_WINDSACK_LAST, 1 },
02619 { GFX_RADAR_INTERNATIONAL_FIRST, GFX_RADAR_INTERNATIONAL_LAST, 3 },
02620 { GFX_RADAR_METROPOLITAN_FIRST, GFX_RADAR_METROPOLITAN_LAST, 3 },
02621 { GFX_RADAR_DISTRICTWE_FIRST, GFX_RADAR_DISTRICTWE_LAST, 3 },
02622 { GFX_WINDSACK_INTERCON_FIRST, GFX_WINDSACK_INTERCON_LAST, 1 }
02623 };
02624
02625 if (IsRailwayStation(tile)) {
02626 AnimateStationTile(tile);
02627 return;
02628 }
02629
02630 StationGfx gfx = GetStationGfx(tile);
02631
02632 for (const AnimData *i = data; i != endof(data); i++) {
02633 if (i->from <= gfx && gfx <= i->to) {
02634 if ((_tick_counter & i->delay) == 0) {
02635 SetStationGfx(tile, gfx < i->to ? gfx + 1 : i->from);
02636 MarkTileDirtyByTile(tile);
02637 }
02638 break;
02639 }
02640 }
02641 }
02642
02643
02644 static bool ClickTile_Station(TileIndex tile)
02645 {
02646 if (IsHangar(tile)) {
02647 ShowDepotWindow(tile, VEH_AIRCRAFT);
02648 } else {
02649 ShowStationViewWindow(GetStationIndex(tile));
02650 }
02651 return true;
02652 }
02653
02654 static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
02655 {
02656 StationID station_id = GetStationIndex(tile);
02657
02658 if (v->type == VEH_TRAIN) {
02659 if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
02660 if (IsRailwayStation(tile) && IsFrontEngine(v) &&
02661 !IsCompatibleTrainStationTile(tile + TileOffsByDiagDir(DirToDiagDir(v->direction)), tile)) {
02662 DiagDirection dir = DirToDiagDir(v->direction);
02663
02664 x &= 0xF;
02665 y &= 0xF;
02666
02667 if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
02668 if (y == TILE_SIZE / 2) {
02669 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
02670 int stop = TILE_SIZE - (v->u.rail.cached_veh_length + 1) / 2;
02671 if (x == stop) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET);
02672 if (x < stop) {
02673 uint16 spd;
02674
02675 v->vehstatus |= VS_TRAIN_SLOWING;
02676 spd = max(0, (stop - x) * 20 - 15);
02677 if (spd < v->cur_speed) v->cur_speed = spd;
02678 }
02679 }
02680 }
02681 } else if (v->type == VEH_ROAD) {
02682 if (v->u.road.state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)v->u.road.state) && v->u.road.frame == 0) {
02683 if (IsRoadStop(tile) && IsRoadVehFront(v)) {
02684
02685 RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile));
02686
02687 if (IsDriveThroughStopTile(tile)) {
02688 if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
02689
02690
02691 byte side = ((DirToDiagDir(v->direction) == ReverseDiagDir(GetRoadStopDir(tile))) == (v->u.road.overtaking == 0)) ? 0 : 1;
02692
02693 if (!rs->IsFreeBay(side)) return VETSB_CANNOT_ENTER;
02694
02695
02696 if (GetRoadStopType(tile) == (IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? ROADSTOP_BUS : ROADSTOP_TRUCK) &&
02697 v->current_order.GetDestination() == GetStationIndex(tile)) {
02698 SetBit(v->u.road.state, RVS_IS_STOPPING);
02699 rs->AllocateDriveThroughBay(side);
02700 }
02701
02702
02703 if (side == 1) SetBit(v->u.road.state, RVS_USING_SECOND_BAY);
02704
02705 SetBit(v->u.road.state, RVS_IN_DT_ROAD_STOP);
02706 return VETSB_CONTINUE;
02707 }
02708
02709
02710
02711 if (rs->IsEntranceBusy() || !rs->HasFreeBay() || RoadVehHasArticPart(v)) return VETSB_CANNOT_ENTER;
02712
02713 SetBit(v->u.road.state, RVS_IN_ROAD_STOP);
02714
02715
02716 uint bay_nr = rs->AllocateBay();
02717 SB(v->u.road.state, RVS_USING_SECOND_BAY, 1, bay_nr);
02718
02719
02720 rs->SetEntranceBusy(true);
02721 }
02722 }
02723 }
02724
02725 return VETSB_CONTINUE;
02726 }
02727
02728
02729 static void StationHandleBigTick(Station *st)
02730 {
02731 UpdateStationAcceptance(st, true);
02732
02733 if (st->facilities == 0 && ++st->delete_ctr >= 8) delete st;
02734
02735 }
02736
02737 static inline void byte_inc_sat(byte *p)
02738 {
02739 byte b = *p + 1;
02740 if (b != 0) *p = b;
02741 }
02742
02743 static void UpdateStationRating(Station *st)
02744 {
02745 bool waiting_changed = false;
02746
02747 byte_inc_sat(&st->time_since_load);
02748 byte_inc_sat(&st->time_since_unload);
02749
02750 GoodsEntry *ge = st->goods;
02751 do {
02752
02753
02754
02755 if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP) && ge->rating < INITIAL_STATION_RATING) {
02756 ge->rating++;
02757 }
02758
02759
02760 if (HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) {
02761 byte_inc_sat(&ge->days_since_pickup);
02762
02763 int rating = 0;
02764
02765 {
02766 int b = ge->last_speed - 85;
02767 if (b >= 0)
02768 rating += b >> 2;
02769 }
02770
02771 {
02772 byte age = ge->last_age;
02773 (age >= 3) ||
02774 (rating += 10, age >= 2) ||
02775 (rating += 10, age >= 1) ||
02776 (rating += 13, true);
02777 }
02778
02779 if (IsValidCompanyID(st->owner) && HasBit(st->town->statues, st->owner)) rating += 26;
02780
02781 {
02782 byte days = ge->days_since_pickup;
02783 if (st->last_vehicle_type == VEH_SHIP) days >>= 2;
02784 (days > 21) ||
02785 (rating += 25, days > 12) ||
02786 (rating += 25, days > 6) ||
02787 (rating += 45, days > 3) ||
02788 (rating += 35, true);
02789 }
02790
02791 uint waiting = ge->cargo.Count();
02792 (rating -= 90, waiting > 1500) ||
02793 (rating += 55, waiting > 1000) ||
02794 (rating += 35, waiting > 600) ||
02795 (rating += 10, waiting > 300) ||
02796 (rating += 20, waiting > 100) ||
02797 (rating += 10, true);
02798
02799 {
02800 int or_ = ge->rating;
02801
02802
02803 ge->rating = rating = or_ + Clamp(Clamp(rating, 0, 255) - or_, -2, 2);
02804
02805
02806
02807 if (rating <= 64 && waiting >= 200) {
02808 int dec = Random() & 0x1F;
02809 if (waiting < 400) dec &= 7;
02810 waiting -= dec + 1;
02811 waiting_changed = true;
02812 }
02813
02814
02815 if (rating <= 127 && waiting != 0) {
02816 uint32 r = Random();
02817 if (rating <= (int)GB(r, 0, 7)) {
02818
02819 waiting = max((int)waiting - (int)GB(r, 8, 2) - 1, 0);
02820 waiting_changed = true;
02821 }
02822 }
02823
02824
02825
02826
02827 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
02828 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
02829 static const uint MAX_WAITING_CARGO = 1 << 15;
02830
02831 if (waiting > WAITING_CARGO_THRESHOLD) {
02832 uint difference = waiting - WAITING_CARGO_THRESHOLD;
02833 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
02834
02835 waiting = min(waiting, MAX_WAITING_CARGO);
02836 waiting_changed = true;
02837 }
02838
02839 if (waiting_changed) ge->cargo.Truncate(waiting);
02840 }
02841 }
02842 } while (++ge != endof(st->goods));
02843
02844 StationID index = st->index;
02845 if (waiting_changed) {
02846 InvalidateWindow(WC_STATION_VIEW, index);
02847 } else {
02848 InvalidateWindowWidget(WC_STATION_VIEW, index, SVW_RATINGLIST);
02849 }
02850 }
02851
02852
02853 static void StationHandleSmallTick(Station *st)
02854 {
02855 if (st->facilities == 0) return;
02856
02857 byte b = st->delete_ctr + 1;
02858 if (b >= 185) b = 0;
02859 st->delete_ctr = b;
02860
02861 if (b == 0) UpdateStationRating(st);
02862 }
02863
02864 void OnTick_Station()
02865 {
02866 if (_game_mode == GM_EDITOR) return;
02867
02868 uint i = _station_tick_ctr;
02869 if (++_station_tick_ctr > GetMaxStationIndex()) _station_tick_ctr = 0;
02870
02871 if (IsValidStationID(i)) StationHandleBigTick(GetStation(i));
02872
02873 Station *st;
02874 FOR_ALL_STATIONS(st) {
02875 StationHandleSmallTick(st);
02876
02877
02878
02879
02880 if ((_tick_counter + st->index) % 250 == 0) {
02881 StationAnimationTrigger(st, st->xy, STAT_ANIM_250_TICKS);
02882 }
02883 }
02884 }
02885
02886 void StationMonthlyLoop()
02887 {
02888
02889 }
02890
02891
02892 void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
02893 {
02894 Station *st;
02895
02896 FOR_ALL_STATIONS(st) {
02897 if (st->owner == owner &&
02898 DistanceManhattan(tile, st->xy) <= radius) {
02899 for (CargoID i = 0; i < NUM_CARGO; i++) {
02900 GoodsEntry *ge = &st->goods[i];
02901
02902 if (ge->acceptance_pickup != 0) {
02903 ge->rating = Clamp(ge->rating + amount, 0, 255);
02904 }
02905 }
02906 }
02907 }
02908 }
02909
02910 static void UpdateStationWaiting(Station *st, CargoID type, uint amount)
02911 {
02912 st->goods[type].cargo.Append(new CargoPacket(st->index, amount));
02913 SetBit(st->goods[type].acceptance_pickup, GoodsEntry::PICKUP);
02914
02915 StationAnimationTrigger(st, st->xy, STAT_ANIM_NEW_CARGO, type);
02916
02917 InvalidateWindow(WC_STATION_VIEW, st->index);
02918 st->MarkTilesDirty(true);
02919 }
02920
02921 static bool IsUniqueStationName(const char *name)
02922 {
02923 const Station *st;
02924
02925 FOR_ALL_STATIONS(st) {
02926 if (st->name != NULL && strcmp(st->name, name) == 0) return false;
02927 }
02928
02929 return true;
02930 }
02931
02938 CommandCost CmdRenameStation(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02939 {
02940 if (!IsValidStationID(p1)) return CMD_ERROR;
02941
02942 Station *st = GetStation(p1);
02943 if (!CheckOwnership(st->owner)) return CMD_ERROR;
02944
02945 bool reset = StrEmpty(text);
02946
02947 if (!reset) {
02948 if (strlen(text) >= MAX_LENGTH_STATION_NAME_BYTES) return CMD_ERROR;
02949 if (!IsUniqueStationName(text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
02950 }
02951
02952 if (flags & DC_EXEC) {
02953 free(st->name);
02954 st->name = reset ? NULL : strdup(text);
02955
02956 UpdateStationVirtCoord(st);
02957 InvalidateWindowData(WC_STATION_LIST, st->owner, 1);
02958 MarkWholeScreenDirty();
02959 }
02960
02961 return CommandCost();
02962 }
02963
02973 void FindStationsAroundTiles(TileIndex tile, int w_prod, int h_prod, StationList *stations)
02974 {
02975
02976 int max_rad = (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED);
02977
02978 for (int dy = -max_rad; dy < h_prod + max_rad; dy++) {
02979 for (int dx = -max_rad; dx < w_prod + max_rad; dx++) {
02980 TileIndex cur_tile = TileAddWrap(tile, dx, dy);
02981 if (cur_tile == INVALID_TILE || !IsTileType(cur_tile, MP_STATION)) continue;
02982
02983 Station *st = GetStationByTile(cur_tile);
02984
02985 if (st->IsBuoy()) continue;
02986
02987 if (_settings_game.station.modified_catchment) {
02988 int rad = st->GetCatchmentRadius();
02989 if (dx < -rad || dx >= rad + w_prod || dy < -rad || dy >= rad + h_prod) continue;
02990 }
02991
02992
02993
02994
02995 stations->Include(st);
02996 }
02997 }
02998 }
02999
03000 uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount)
03001 {
03002 Station *st1 = NULL;
03003 Station *st2 = NULL;
03004 uint best_rating1 = 0;
03005 uint best_rating2 = 0;
03006
03007 StationList all_stations;
03008 FindStationsAroundTiles(tile, w, h, &all_stations);
03009 for (Station **st_iter = all_stations.Begin(); st_iter != all_stations.End(); ++st_iter) {
03010 Station *st = *st_iter;
03011
03012
03013 if (st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) continue;
03014
03015 if (st->goods[type].rating == 0) continue;
03016
03017 if (_settings_game.order.selectgoods && st->goods[type].last_speed == 0) continue;
03018
03019 if (IsCargoInClass(type, CC_PASSENGERS)) {
03020 if (st->facilities == FACIL_TRUCK_STOP) continue;
03021 } else {
03022 if (st->facilities == FACIL_BUS_STOP) continue;
03023 }
03024
03025
03026 if (st1 == NULL || st->goods[type].rating >= best_rating1) {
03027 st2 = st1; best_rating2 = best_rating1; st1 = st; best_rating1 = st->goods[type].rating;
03028 } else if (st2 == NULL || st->goods[type].rating >= best_rating2) {
03029 st2 = st; best_rating2 = st->goods[type].rating;
03030 }
03031 }
03032
03033
03034 if (st1 == NULL) return 0;
03035
03036 if (st2 == NULL) {
03037
03038 uint moved = amount * best_rating1 / 256 + 1;
03039 UpdateStationWaiting(st1, type, moved);
03040 return moved;
03041 }
03042
03043
03044 assert(st1 != NULL);
03045 assert(st2 != NULL);
03046 assert(best_rating1 != 0 || best_rating2 != 0);
03047
03048
03049 best_rating2 >>= 1;
03050
03051
03052 uint t = (best_rating1 * (amount + 1)) / (best_rating1 + best_rating2);
03053
03054 uint moved = 0;
03055 if (t != 0) {
03056 moved = t * best_rating1 / 256 + 1;
03057 amount -= t;
03058 UpdateStationWaiting(st1, type, moved);
03059 }
03060
03061 if (amount != 0) {
03062 amount = amount * best_rating2 / 256 + 1;
03063 moved += amount;
03064 UpdateStationWaiting(st2, type, amount);
03065 }
03066
03067 return moved;
03068 }
03069
03070 void BuildOilRig(TileIndex tile)
03071 {
03072 if (!Station::CanAllocateItem()) {
03073 DEBUG(misc, 0, "Can't allocate station for oilrig at 0x%X, reverting to oilrig only", tile);
03074 return;
03075 }
03076
03077 Station *st = new Station(tile);
03078 st->town = ClosestTownFromTile(tile, UINT_MAX);
03079 st->sign.width_1 = 0;
03080
03081 st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
03082
03083 assert(IsTileType(tile, MP_INDUSTRY));
03084 MakeOilrig(tile, st->index, GetWaterClass(tile));
03085
03086 st->owner = OWNER_NONE;
03087 st->airport_flags = 0;
03088 st->airport_type = AT_OILRIG;
03089 st->xy = tile;
03090 st->bus_stops = NULL;
03091 st->truck_stops = NULL;
03092 st->airport_tile = tile;
03093 st->dock_tile = tile;
03094 st->train_tile = INVALID_TILE;
03095 st->had_vehicle_of_type = 0;
03096 st->time_since_load = 255;
03097 st->time_since_unload = 255;
03098 st->delete_ctr = 0;
03099 st->last_vehicle_type = VEH_INVALID;
03100 st->facilities = FACIL_AIRPORT | FACIL_DOCK;
03101 st->build_date = _date;
03102
03103 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
03104
03105 for (CargoID j = 0; j < NUM_CARGO; j++) {
03106 st->goods[j].acceptance_pickup = 0;
03107 st->goods[j].days_since_pickup = 255;
03108 st->goods[j].rating = INITIAL_STATION_RATING;
03109 st->goods[j].last_speed = 0;
03110 st->goods[j].last_age = 255;
03111 }
03112
03113 UpdateStationVirtCoordDirty(st);
03114 UpdateStationAcceptance(st, false);
03115 }
03116
03117 void DeleteOilRig(TileIndex tile)
03118 {
03119 Station *st = GetStationByTile(tile);
03120
03121 MakeWaterKeepingClass(tile, OWNER_NONE);
03122 MarkTileDirtyByTile(tile);
03123
03124 st->dock_tile = INVALID_TILE;
03125 st->airport_tile = INVALID_TILE;
03126 st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
03127 st->airport_flags = 0;
03128
03129 st->rect.AfterRemoveTile(st, tile);
03130
03131 UpdateStationVirtCoordDirty(st);
03132 if (st->facilities == 0) delete st;
03133 }
03134
03135 static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
03136 {
03137 if (IsDriveThroughStopTile(tile)) {
03138 for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
03139
03140 if (GetRoadOwner(tile, rt) == old_owner) {
03141 SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
03142 }
03143 }
03144 }
03145
03146 if (!IsTileOwner(tile, old_owner)) return;
03147
03148 if (new_owner != INVALID_OWNER) {
03149
03150 SetTileOwner(tile, new_owner);
03151 InvalidateWindowClassesData(WC_STATION_LIST, 0);
03152 } else {
03153 if (IsDriveThroughStopTile(tile)) {
03154
03155 DoCommand(tile, 0, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, DC_EXEC | DC_BANKRUPT, CMD_REMOVE_ROAD_STOP);
03156 assert(IsTileType(tile, MP_ROAD));
03157
03158 ChangeTileOwner(tile, old_owner, new_owner);
03159 } else {
03160 DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
03161
03162
03163
03164 if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
03165 }
03166 }
03167 }
03168
03177 static bool CanRemoveRoadWithStop(TileIndex tile, DoCommandFlag flags)
03178 {
03179 Owner road_owner = _current_company;
03180 Owner tram_owner = _current_company;
03181
03182 RoadTypes rts = GetRoadTypes(tile);
03183 if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
03184 if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
03185
03186 if ((road_owner != OWNER_TOWN && !CheckOwnership(road_owner)) || !CheckOwnership(tram_owner)) return false;
03187
03188 return road_owner != OWNER_TOWN || CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_ROAD), OWNER_TOWN, ROADTYPE_ROAD, flags);
03189 }
03190
03191 static CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
03192 {
03193 if (flags & DC_AUTO) {
03194 switch (GetStationType(tile)) {
03195 case STATION_RAIL: return_cmd_error(STR_300B_MUST_DEMOLISH_RAILROAD);
03196 case STATION_AIRPORT: return_cmd_error(STR_300E_MUST_DEMOLISH_AIRPORT_FIRST);
03197 case STATION_TRUCK: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_MUST_DEMOLISH_CARGO_TRAM_STATION : STR_3047_MUST_DEMOLISH_TRUCK_STATION);
03198 case STATION_BUS: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_MUST_DEMOLISH_PASSENGER_TRAM_STATION : STR_3046_MUST_DEMOLISH_BUS_STATION);
03199 case STATION_BUOY: return_cmd_error(STR_306A_BUOY_IN_THE_WAY);
03200 case STATION_DOCK: return_cmd_error(STR_304D_MUST_DEMOLISH_DOCK_FIRST);
03201 case STATION_OILRIG:
03202 SetDParam(0, STR_4807_OIL_RIG);
03203 return_cmd_error(STR_4800_IN_THE_WAY);
03204 }
03205 }
03206
03207 Station *st = GetStationByTile(tile);
03208
03209 switch (GetStationType(tile)) {
03210 case STATION_RAIL: return RemoveRailroadStation(st, tile, flags);
03211 case STATION_AIRPORT: return RemoveAirport(st, flags);
03212 case STATION_TRUCK:
03213 if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags))
03214 return_cmd_error(STR_3047_MUST_DEMOLISH_TRUCK_STATION);
03215 return RemoveRoadStop(st, flags, tile);
03216 case STATION_BUS:
03217 if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags))
03218 return_cmd_error(STR_3046_MUST_DEMOLISH_BUS_STATION);
03219 return RemoveRoadStop(st, flags, tile);
03220 case STATION_BUOY: return RemoveBuoy(st, flags);
03221 case STATION_DOCK: return RemoveDock(st, flags);
03222 default: break;
03223 }
03224
03225 return CMD_ERROR;
03226 }
03227
03228 void InitializeStations()
03229 {
03230
03231 _Station_pool.CleanPool();
03232 _Station_pool.AddBlockToPool();
03233
03234
03235 _RoadStop_pool.CleanPool();
03236 _RoadStop_pool.AddBlockToPool();
03237
03238 _station_tick_ctr = 0;
03239 }
03240
03241 static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
03242 {
03243 if (_settings_game.construction.build_on_slopes && AutoslopeEnabled()) {
03244
03245
03246
03247 if (!IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new))) {
03248 switch (GetStationType(tile)) {
03249 case STATION_RAIL: {
03250 DiagDirection direction = AxisToDiagDir(GetRailStationAxis(tile));
03251 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
03252 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
03253 return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform);
03254 }
03255
03256 case STATION_AIRPORT:
03257 return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform);
03258
03259 case STATION_TRUCK:
03260 case STATION_BUS: {
03261 DiagDirection direction = GetRoadStopDir(tile);
03262 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
03263 if (IsDriveThroughStopTile(tile)) {
03264 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
03265 }
03266 return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform);
03267 }
03268
03269 default: break;
03270 }
03271 }
03272 }
03273 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
03274 }
03275
03276
03277 extern const TileTypeProcs _tile_type_station_procs = {
03278 DrawTile_Station,
03279 GetSlopeZ_Station,
03280 ClearTile_Station,
03281 GetAcceptedCargo_Station,
03282 GetTileDesc_Station,
03283 GetTileTrackStatus_Station,
03284 ClickTile_Station,
03285 AnimateTile_Station,
03286 TileLoop_Station,
03287 ChangeTileOwner_Station,
03288 NULL,
03289 VehicleEnter_Station,
03290 GetFoundation_Station,
03291 TerraformTile_Station,
03292 };