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 Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
01636 Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
01637 CommandCost ret = RemoveRoadStop(st, flags, tile);
01638
01639
01640 if ((flags & DC_EXEC) && CmdSucceeded(ret) && is_drive_through) {
01641
01642
01643
01644 MakeRoadNormal(tile, road_bits, rts, ClosestTownFromTile(tile, UINT_MAX)->index,
01645 road_owner, tram_owner);
01646 }
01647
01648 return ret;
01649 }
01650
01651
01652
01653
01654 static const byte _airport_sections_country[] = {
01655 54, 53, 52, 65,
01656 58, 57, 56, 55,
01657 64, 63, 63, 62
01658 };
01659
01660
01661 static const byte _airport_sections_town[] = {
01662 31, 9, 33, 9, 9, 32,
01663 27, 36, 29, 34, 8, 10,
01664 30, 11, 35, 13, 20, 21,
01665 51, 12, 14, 17, 19, 28,
01666 38, 13, 15, 16, 18, 39,
01667 26, 22, 23, 24, 25, 26
01668 };
01669
01670
01671 static const byte _airport_sections_metropolitan[] = {
01672 31, 9, 33, 9, 9, 32,
01673 27, 36, 29, 34, 8, 10,
01674 30, 11, 35, 13, 20, 21,
01675 102, 8, 8, 8, 8, 28,
01676 83, 84, 84, 84, 84, 83,
01677 26, 23, 23, 23, 23, 26
01678 };
01679
01680
01681 static const byte _airport_sections_international[] = {
01682 88, 89, 89, 89, 89, 89, 88,
01683 51, 8, 8, 8, 8, 8, 32,
01684 30, 8, 11, 27, 11, 8, 10,
01685 32, 8, 11, 27, 11, 8, 114,
01686 87, 8, 11, 85, 11, 8, 114,
01687 87, 8, 8, 8, 8, 8, 90,
01688 26, 23, 23, 23, 23, 23, 26
01689 };
01690
01691
01692 static const byte _airport_sections_intercontinental[] = {
01693 102, 120, 89, 89, 89, 89, 89, 89, 118,
01694 120, 23, 23, 23, 23, 23, 23, 119, 117,
01695 87, 54, 87, 8, 8, 8, 8, 51, 117,
01696 87, 162, 87, 85, 116, 116, 8, 9, 10,
01697 87, 8, 8, 11, 31, 11, 8, 160, 32,
01698 32, 160, 8, 11, 27, 11, 8, 8, 10,
01699 87, 8, 8, 11, 30, 11, 8, 8, 10,
01700 87, 142, 8, 11, 29, 11, 10, 163, 10,
01701 87, 164, 87, 8, 8, 8, 10, 37, 117,
01702 87, 120, 89, 89, 89, 89, 89, 89, 119,
01703 121, 23, 23, 23, 23, 23, 23, 119, 37
01704 };
01705
01706
01707
01708 static const byte _airport_sections_commuter[] = {
01709 85, 30, 115, 115, 32,
01710 87, 8, 8, 8, 10,
01711 87, 11, 11, 11, 10,
01712 26, 23, 23, 23, 26
01713 };
01714
01715
01716 static const byte _airport_sections_heliport[] = {
01717 66,
01718 };
01719
01720
01721 static const byte _airport_sections_helidepot[] = {
01722 124, 32,
01723 122, 123
01724 };
01725
01726
01727 static const byte _airport_sections_helistation[] = {
01728 32, 134, 159, 158,
01729 161, 142, 142, 157
01730 };
01731
01732 static const byte * const _airport_sections[] = {
01733 _airport_sections_country,
01734 _airport_sections_town,
01735 _airport_sections_heliport,
01736 _airport_sections_metropolitan,
01737 _airport_sections_international,
01738 _airport_sections_commuter,
01739 _airport_sections_helidepot,
01740 _airport_sections_intercontinental,
01741 _airport_sections_helistation
01742 };
01743
01751 static uint GetMinimalAirportDistanceToTile(const AirportFTAClass *afc, TileIndex town_tile, TileIndex airport_tile)
01752 {
01753 uint ttx = TileX(town_tile);
01754 uint tty = TileY(town_tile);
01755
01756 uint atx = TileX(airport_tile);
01757 uint aty = TileY(airport_tile);
01758
01759 uint btx = TileX(airport_tile) + afc->size_x - 1;
01760 uint bty = TileY(airport_tile) + afc->size_y - 1;
01761
01762
01763
01764
01765 uint dx = ttx < atx ? atx - ttx : (ttx <= btx ? 0 : ttx - btx);
01766 uint dy = tty < aty ? aty - tty : (tty <= bty ? 0 : tty - bty);
01767
01768 return dx + dy;
01769 }
01770
01779 uint8 GetAirportNoiseLevelForTown(const AirportFTAClass *afc, TileIndex town_tile, TileIndex tile)
01780 {
01781
01782
01783 if (afc->noise_level < 2) return afc->noise_level;
01784
01785 uint distance = GetMinimalAirportDistanceToTile(afc, town_tile, tile);
01786
01787
01788
01789
01790
01791 uint8 town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
01792
01793
01794
01795 uint noise_reduction = distance / town_tolerance_distance;
01796
01797
01798
01799 return noise_reduction >= afc->noise_level ? 1 : afc->noise_level - noise_reduction;
01800 }
01801
01809 Town *AirportGetNearestTown(const AirportFTAClass *afc, TileIndex airport_tile)
01810 {
01811 Town *t, *nearest = NULL;
01812 uint add = afc->size_x + afc->size_y - 2;
01813 uint mindist = UINT_MAX - add;
01814 FOR_ALL_TOWNS(t) {
01815 if (DistanceManhattan(t->xy, airport_tile) < mindist + add) {
01816 uint dist = GetMinimalAirportDistanceToTile(afc, t->xy, airport_tile);
01817 if (dist < mindist) {
01818 nearest = t;
01819 mindist = dist;
01820 }
01821 }
01822 }
01823
01824 return nearest;
01825 }
01826
01827
01829 void UpdateAirportsNoise()
01830 {
01831 Town *t;
01832 const Station *st;
01833
01834 FOR_ALL_TOWNS(t) t->noise_reached = 0;
01835
01836 FOR_ALL_STATIONS(st) {
01837 if (st->airport_tile != INVALID_TILE) {
01838 const AirportFTAClass *afc = GetAirport(st->airport_type);
01839 Town *nearest = AirportGetNearestTown(afc, st->airport_tile);
01840 nearest->noise_reached += GetAirportNoiseLevelForTown(afc, nearest->xy, st->airport_tile);
01841 }
01842 }
01843 }
01844
01845
01854 CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01855 {
01856 bool airport_upgrade = true;
01857 StationID station_to_join = GB(p2, 16, 16);
01858 bool distant_join = (station_to_join != INVALID_STATION);
01859
01860 if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR;
01861
01862
01863 if (p1 > lengthof(_airport_sections) || !HasBit(GetValidAirports(), p1)) return CMD_ERROR;
01864
01865 if (!CheckIfAuthorityAllowsNewStation(tile, flags)) {
01866 return CMD_ERROR;
01867 }
01868
01869 Town *t = ClosestTownFromTile(tile, UINT_MAX);
01870 const AirportFTAClass *afc = GetAirport(p1);
01871 int w = afc->size_x;
01872 int h = afc->size_y;
01873 Station *st = NULL;
01874
01875 if (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread) {
01876 _error_message = STR_306C_STATION_TOO_SPREAD_OUT;
01877 return CMD_ERROR;
01878 }
01879
01880 CommandCost cost = CheckFlatLandBelow(tile, w, h, flags, 0, NULL);
01881 if (CmdFailed(cost)) return cost;
01882
01883
01884 Town *nearest = AirportGetNearestTown(afc, tile);
01885 uint newnoise_level = GetAirportNoiseLevelForTown(afc, nearest->xy, tile);
01886
01887
01888 StringID authority_refuse_message = STR_NULL;
01889
01890 if (_settings_game.economy.station_noise_level) {
01891
01892 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
01893 authority_refuse_message = STR_LOCAL_AUTHORITY_REFUSES_NOISE;
01894 }
01895 } else {
01896 uint num = 0;
01897 const Station *st;
01898 FOR_ALL_STATIONS(st) {
01899 if (st->town == t && st->facilities & FACIL_AIRPORT && st->airport_type != AT_OILRIG) num++;
01900 }
01901 if (num >= 2) {
01902 authority_refuse_message = STR_2035_LOCAL_AUTHORITY_REFUSES;
01903 }
01904 }
01905
01906 if (authority_refuse_message != STR_NULL) {
01907 SetDParam(0, t->index);
01908 return_cmd_error(authority_refuse_message);
01909 }
01910
01911 if (!_settings_game.station.adjacent_stations || !HasBit(p2, 0)) {
01912 st = GetStationAround(tile, w, h, INVALID_STATION);
01913 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
01914 } else {
01915 st = NULL;
01916 }
01917
01918
01919 if (st == NULL && distant_join) st = GetStation(station_to_join);
01920
01921
01922 if (st == NULL) st = GetClosestDeletedStation(tile);
01923
01924 if (st != NULL) {
01925 if (st->owner != _current_company) {
01926 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
01927 }
01928
01929 if (!st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TEST)) return CMD_ERROR;
01930
01931 if (st->airport_tile != INVALID_TILE) {
01932 return_cmd_error(STR_300D_TOO_CLOSE_TO_ANOTHER_AIRPORT);
01933 }
01934 } else {
01935 airport_upgrade = false;
01936
01937
01938 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
01939
01940 if (flags & DC_EXEC) {
01941 st = new Station(tile);
01942
01943 st->town = t;
01944 st->string_id = GenerateStationName(st, tile, !(afc->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_HELIPORT : STATIONNAMING_AIRPORT);
01945
01946 if (IsValidCompanyID(_current_company)) {
01947 SetBit(st->town->have_ratings, _current_company);
01948 }
01949 st->sign.width_1 = 0;
01950 }
01951 }
01952
01953 cost.AddCost(_price.build_airport * w * h);
01954
01955 if (flags & DC_EXEC) {
01956
01957 nearest->noise_reached += newnoise_level;
01958
01959 st->airport_tile = tile;
01960 st->AddFacility(FACIL_AIRPORT, tile);
01961 st->airport_type = (byte)p1;
01962 st->airport_flags = 0;
01963
01964 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
01965
01966
01967
01968
01969
01970
01971
01972
01973 if (airport_upgrade) UpdateAirplanesOnNewStation(st);
01974
01975 {
01976 const byte *b = _airport_sections[p1];
01977
01978 BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
01979 MakeAirport(tile_cur, st->owner, st->index, *b - ((*b < 67) ? 8 : 24));
01980 b++;
01981 } END_TILE_LOOP(tile_cur, w, h, tile)
01982 }
01983
01984 UpdateStationVirtCoordDirty(st);
01985 UpdateStationAcceptance(st, false);
01986 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
01987 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
01988 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_PLANES);
01989
01990 if (_settings_game.economy.station_noise_level) {
01991 InvalidateWindow(WC_TOWN_VIEW, st->town->index);
01992 }
01993 }
01994
01995 return cost;
01996 }
01997
01998 static CommandCost RemoveAirport(Station *st, DoCommandFlag flags)
01999 {
02000 if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) {
02001 return CMD_ERROR;
02002 }
02003
02004 TileIndex tile = st->airport_tile;
02005
02006 const AirportFTAClass *afc = st->Airport();
02007 int w = afc->size_x;
02008 int h = afc->size_y;
02009
02010 CommandCost cost(EXPENSES_CONSTRUCTION, w * h * _price.remove_airport);
02011
02012 const Vehicle *v;
02013 FOR_ALL_VEHICLES(v) {
02014 if (!(v->type == VEH_AIRCRAFT && IsNormalAircraft(v))) continue;
02015
02016 if (v->u.air.targetairport == st->index && v->u.air.state != FLYING) return CMD_ERROR;
02017 }
02018
02019 BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
02020 if (!EnsureNoVehicleOnGround(tile_cur)) return CMD_ERROR;
02021
02022 if (flags & DC_EXEC) {
02023 DeleteAnimatedTile(tile_cur);
02024 DoClearSquare(tile_cur);
02025 }
02026 } END_TILE_LOOP(tile_cur, w, h, tile)
02027
02028 if (flags & DC_EXEC) {
02029 for (uint i = 0; i < afc->nof_depots; ++i) {
02030 DeleteWindowById(
02031 WC_VEHICLE_DEPOT, tile + ToTileIndexDiff(afc->airport_depots[i])
02032 );
02033 }
02034
02035
02036
02037
02038 Town *nearest = AirportGetNearestTown(afc, tile);
02039 nearest->noise_reached -= GetAirportNoiseLevelForTown(afc, nearest->xy, tile);
02040
02041 st->rect.AfterRemoveRect(st, tile, w, h);
02042
02043 st->airport_tile = INVALID_TILE;
02044 st->facilities &= ~FACIL_AIRPORT;
02045
02046 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_PLANES);
02047
02048 if (_settings_game.economy.station_noise_level) {
02049 InvalidateWindow(WC_TOWN_VIEW, st->town->index);
02050 }
02051
02052 UpdateStationVirtCoordDirty(st);
02053 DeleteStationIfEmpty(st);
02054 }
02055
02056 return cost;
02057 }
02058
02065 CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02066 {
02067 if (!IsWaterTile(tile) || tile == 0) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02068 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
02069
02070 if (GetTileSlope(tile, NULL) != SLOPE_FLAT) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02071
02072
02073 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
02074
02075 if (flags & DC_EXEC) {
02076 Station *st = new Station(tile);
02077
02078 st->town = ClosestTownFromTile(tile, UINT_MAX);
02079 st->string_id = GenerateStationName(st, tile, STATIONNAMING_BUOY);
02080
02081 if (IsValidCompanyID(_current_company)) {
02082 SetBit(st->town->have_ratings, _current_company);
02083 }
02084 st->sign.width_1 = 0;
02085 st->dock_tile = tile;
02086 st->facilities |= FACIL_DOCK;
02087
02088
02089 st->had_vehicle_of_type |= HVOT_BUOY;
02090 st->owner = OWNER_NONE;
02091
02092 st->build_date = _date;
02093
02094 MakeBuoy(tile, st->index, GetWaterClass(tile));
02095
02096 UpdateStationVirtCoordDirty(st);
02097 UpdateStationAcceptance(st, false);
02098 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
02099 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02100 }
02101
02102 return CommandCost(EXPENSES_CONSTRUCTION, _price.build_dock);
02103 }
02104
02111 bool HasStationInUse(StationID station, CompanyID company)
02112 {
02113 const Vehicle *v;
02114 FOR_ALL_VEHICLES(v) {
02115 if (company == INVALID_COMPANY || v->owner == company) {
02116 const Order *order;
02117 FOR_VEHICLE_ORDERS(v, order) {
02118 if (order->IsType(OT_GOTO_STATION) && order->GetDestination() == station) {
02119 return true;
02120 }
02121 }
02122 }
02123 }
02124 return false;
02125 }
02126
02127 static CommandCost RemoveBuoy(Station *st, DoCommandFlag flags)
02128 {
02129
02130 if (!IsValidCompanyID(_current_company)) return_cmd_error(INVALID_STRING_ID);
02131
02132 TileIndex tile = st->dock_tile;
02133
02134 if (HasStationInUse(st->index, INVALID_COMPANY)) return_cmd_error(STR_BUOY_IS_IN_USE);
02135
02136 if (!(flags & DC_BANKRUPT) && !EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
02137
02138 if (flags & DC_EXEC) {
02139 st->dock_tile = INVALID_TILE;
02140
02141
02142 st->facilities &= ~FACIL_DOCK;
02143 st->had_vehicle_of_type &= ~HVOT_BUOY;
02144
02145 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02146
02147
02148
02149
02150 MakeWaterKeepingClass(tile, GetTileOwner(tile));
02151 MarkTileDirtyByTile(tile);
02152
02153 UpdateStationVirtCoordDirty(st);
02154 DeleteStationIfEmpty(st);
02155 }
02156
02157 return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_truck_station);
02158 }
02159
02160 static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
02161 {-1, 0},
02162 { 0, 0},
02163 { 0, 0},
02164 { 0, -1}
02165 };
02166 static const byte _dock_w_chk[4] = { 2, 1, 2, 1 };
02167 static const byte _dock_h_chk[4] = { 1, 2, 1, 2 };
02168
02175 CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02176 {
02177 StationID station_to_join = GB(p2, 16, 16);
02178 bool distant_join = (station_to_join != INVALID_STATION);
02179
02180 if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR;
02181
02182 DiagDirection direction = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
02183 if (direction == INVALID_DIAGDIR) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02184 direction = ReverseDiagDir(direction);
02185
02186
02187 if (IsWaterTile(tile)) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02188
02189 if (!CheckIfAuthorityAllowsNewStation(tile, flags)) return CMD_ERROR;
02190
02191 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
02192
02193 if (CmdFailed(DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR))) return CMD_ERROR;
02194
02195 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
02196
02197 if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
02198 return_cmd_error(STR_304B_SITE_UNSUITABLE);
02199 }
02200
02201 if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
02202
02203
02204 WaterClass wc = GetWaterClass(tile_cur);
02205
02206 if (CmdFailed(DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR))) return CMD_ERROR;
02207
02208 tile_cur += TileOffsByDiagDir(direction);
02209 if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
02210 return_cmd_error(STR_304B_SITE_UNSUITABLE);
02211 }
02212
02213
02214 Station *st = NULL;
02215
02216 if (!_settings_game.station.adjacent_stations || !HasBit(p1, 0)) {
02217 st = GetStationAround(
02218 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02219 _dock_w_chk[direction], _dock_h_chk[direction], INVALID_STATION);
02220 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
02221 }
02222
02223
02224 if (st == NULL && distant_join) st = GetStation(station_to_join);
02225
02226
02227 if (st == NULL) st = GetClosestDeletedStation(tile);
02228
02229 if (st != NULL) {
02230 if (st->owner != _current_company) {
02231 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
02232 }
02233
02234 if (!st->rect.BeforeAddRect(
02235 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02236 _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TEST)) return CMD_ERROR;
02237
02238 if (st->dock_tile != INVALID_TILE) return_cmd_error(STR_304C_TOO_CLOSE_TO_ANOTHER_DOCK);
02239 } else {
02240
02241 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
02242
02243 if (flags & DC_EXEC) {
02244 st = new Station(tile);
02245
02246 st->town = ClosestTownFromTile(tile, UINT_MAX);
02247 st->string_id = GenerateStationName(st, tile, STATIONNAMING_DOCK);
02248
02249 if (IsValidCompanyID(_current_company)) {
02250 SetBit(st->town->have_ratings, _current_company);
02251 }
02252 }
02253 }
02254
02255 if (flags & DC_EXEC) {
02256 st->dock_tile = tile;
02257 st->AddFacility(FACIL_DOCK, tile);
02258
02259 st->rect.BeforeAddRect(
02260 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02261 _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TRY);
02262
02263 MakeDock(tile, st->owner, st->index, direction, wc);
02264
02265 UpdateStationVirtCoordDirty(st);
02266 UpdateStationAcceptance(st, false);
02267 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
02268 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
02269 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02270 }
02271
02272 return CommandCost(EXPENSES_CONSTRUCTION, _price.build_dock);
02273 }
02274
02275 static CommandCost RemoveDock(Station *st, DoCommandFlag flags)
02276 {
02277 if (!CheckOwnership(st->owner)) return CMD_ERROR;
02278
02279 TileIndex tile1 = st->dock_tile;
02280 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
02281
02282 if (!EnsureNoVehicleOnGround(tile1)) return CMD_ERROR;
02283 if (!EnsureNoVehicleOnGround(tile2)) return CMD_ERROR;
02284
02285 if (flags & DC_EXEC) {
02286 DoClearSquare(tile1);
02287 MakeWaterKeepingClass(tile2, st->owner);
02288
02289 st->rect.AfterRemoveTile(st, tile1);
02290 st->rect.AfterRemoveTile(st, tile2);
02291
02292 MarkTileDirtyByTile(tile2);
02293
02294 st->dock_tile = INVALID_TILE;
02295 st->facilities &= ~FACIL_DOCK;
02296
02297 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02298 UpdateStationVirtCoordDirty(st);
02299 DeleteStationIfEmpty(st);
02300 }
02301
02302 return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_dock);
02303 }
02304
02305 #include "table/station_land.h"
02306
02307 const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx)
02308 {
02309 return &_station_display_datas[st][gfx];
02310 }
02311
02312 static void DrawTile_Station(TileInfo *ti)
02313 {
02314 const DrawTileSprites *t = NULL;
02315 RoadTypes roadtypes;
02316 int32 total_offset;
02317 int32 custom_ground_offset;
02318
02319 if (IsRailwayStation(ti->tile)) {
02320 const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
02321 roadtypes = ROADTYPES_NONE;
02322 total_offset = rti->total_offset;
02323 custom_ground_offset = rti->custom_ground_offset;
02324 } else {
02325 roadtypes = IsRoadStop(ti->tile) ? GetRoadTypes(ti->tile) : ROADTYPES_NONE;
02326 total_offset = 0;
02327 custom_ground_offset = 0;
02328 }
02329 uint32 relocation = 0;
02330 const Station *st = NULL;
02331 const StationSpec *statspec = NULL;
02332 Owner owner = GetTileOwner(ti->tile);
02333
02334 SpriteID palette;
02335 if (IsValidCompanyID(owner)) {
02336 palette = COMPANY_SPRITE_COLOUR(owner);
02337 } else {
02338
02339 palette = PALETTE_TO_GREY;
02340 }
02341
02342
02343 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile))
02344 DrawFoundation(ti, FOUNDATION_LEVELED);
02345
02346 if (IsCustomStationSpecIndex(ti->tile)) {
02347
02348 st = GetStationByTile(ti->tile);
02349 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
02350
02351 if (statspec != NULL) {
02352 uint tile = GetStationGfx(ti->tile);
02353
02354 relocation = GetCustomStationRelocation(statspec, st, ti->tile);
02355
02356 if (HasBit(statspec->callbackmask, CBM_STATION_SPRITE_LAYOUT)) {
02357 uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile);
02358 if (callback != CALLBACK_FAILED) tile = (callback & ~1) + GetRailStationAxis(ti->tile);
02359 }
02360
02361
02362 if (statspec->renderdata != NULL) {
02363 t = &statspec->renderdata[tile < statspec->tiles ? tile : (uint)GetRailStationAxis(ti->tile)];
02364 }
02365 }
02366 }
02367
02368 if (t == NULL || t->seq == NULL) t = &_station_display_datas[GetStationType(ti->tile)][GetStationGfx(ti->tile)];
02369
02370
02371 if (IsBuoy(ti->tile) || IsDock(ti->tile) || (IsOilRig(ti->tile) && GetWaterClass(ti->tile) != WATER_CLASS_INVALID)) {
02372 if (ti->tileh == SLOPE_FLAT) {
02373 DrawWaterClassGround(ti);
02374 } else {
02375 assert(IsDock(ti->tile));
02376 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
02377 WaterClass wc = GetWaterClass(water_tile);
02378 if (wc == WATER_CLASS_SEA) {
02379 DrawShoreTile(ti->tileh);
02380 } else {
02381 DrawClearLandTile(ti, 3);
02382 }
02383 }
02384 } else {
02385 SpriteID image = t->ground.sprite;
02386 SpriteID pal = t->ground.pal;
02387 if (HasBit(image, SPRITE_MODIFIER_USE_OFFSET)) {
02388 image += GetCustomStationGroundRelocation(statspec, st, ti->tile);
02389 image += custom_ground_offset;
02390 } else {
02391 image += total_offset;
02392 }
02393 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
02394
02395
02396 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && IsRailwayStation(ti->tile) && GetRailwayStationReservation(ti->tile)) {
02397 const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
02398 DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH);
02399 }
02400 }
02401
02402 if (IsRailwayStation(ti->tile) && HasCatenaryDrawn(GetRailType(ti->tile)) && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
02403
02404 if (HasBit(roadtypes, ROADTYPE_TRAM)) {
02405 Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
02406 DrawGroundSprite((HasBit(roadtypes, ROADTYPE_ROAD) ? SPR_TRAMWAY_OVERLAY : SPR_TRAMWAY_TRAM) + (axis ^ 1), PAL_NONE);
02407 DrawTramCatenary(ti, axis == AXIS_X ? ROAD_X : ROAD_Y);
02408 }
02409
02410 const DrawTileSeqStruct *dtss;
02411 foreach_draw_tile_seq(dtss, t->seq) {
02412 SpriteID image = dtss->image.sprite;
02413
02414
02415 if (IsInvisibilitySet(TO_BUILDINGS) && !HasBit(image, SPRITE_MODIFIER_OPAQUE)) return;
02416
02417 if (relocation == 0 || HasBit(image, SPRITE_MODIFIER_USE_OFFSET)) {
02418 image += total_offset;
02419 } else {
02420 image += relocation;
02421 }
02422
02423 SpriteID pal = SpriteLayoutPaletteTransform(image, dtss->image.pal, palette);
02424
02425 if ((byte)dtss->delta_z != 0x80) {
02426 AddSortableSpriteToDraw(
02427 image, pal,
02428 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
02429 dtss->size_x, dtss->size_y,
02430 dtss->size_z, ti->z + dtss->delta_z,
02431 !HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_BUILDINGS)
02432 );
02433 } else {
02434
02435 AddChildSpriteScreen(image, pal, dtss->delta_x, dtss->delta_y, !HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_BUILDINGS));
02436 }
02437 }
02438 }
02439
02440 void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
02441 {
02442 int32 total_offset = 0;
02443 SpriteID pal = COMPANY_SPRITE_COLOUR(_local_company);
02444 const DrawTileSprites *t = &_station_display_datas[st][image];
02445
02446 if (railtype != INVALID_RAILTYPE) {
02447 const RailtypeInfo *rti = GetRailTypeInfo(railtype);
02448 total_offset = rti->total_offset;
02449 }
02450
02451 SpriteID img = t->ground.sprite;
02452 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
02453
02454 if (roadtype == ROADTYPE_TRAM) {
02455 DrawSprite(SPR_TRAMWAY_TRAM + (t->ground.sprite == SPR_ROAD_PAVED_STRAIGHT_X ? 1 : 0), PAL_NONE, x, y);
02456 }
02457
02458 const DrawTileSeqStruct *dtss;
02459 foreach_draw_tile_seq(dtss, t->seq) {
02460 Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z);
02461 DrawSprite(dtss->image.sprite + total_offset, pal, x + pt.x, y + pt.y);
02462 }
02463 }
02464
02465 static uint GetSlopeZ_Station(TileIndex tile, uint x, uint y)
02466 {
02467 return GetTileMaxZ(tile);
02468 }
02469
02470 static Foundation GetFoundation_Station(TileIndex tile, Slope tileh)
02471 {
02472 return FlatteningFoundation(tileh);
02473 }
02474
02475 static void GetAcceptedCargo_Station(TileIndex tile, AcceptedCargo ac)
02476 {
02477
02478 }
02479
02480 static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
02481 {
02482 td->owner[0] = GetTileOwner(tile);
02483 if (IsDriveThroughStopTile(tile)) {
02484 Owner road_owner = INVALID_OWNER;
02485 Owner tram_owner = INVALID_OWNER;
02486 RoadTypes rts = GetRoadTypes(tile);
02487 if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
02488 if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
02489
02490
02491 if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) ||
02492 (road_owner != INVALID_OWNER && road_owner != td->owner[0])) {
02493 uint i = 1;
02494 if (road_owner != INVALID_OWNER) {
02495 td->owner_type[i] = STR_ROAD_OWNER;
02496 td->owner[i] = road_owner;
02497 i++;
02498 }
02499 if (tram_owner != INVALID_OWNER) {
02500 td->owner_type[i] = STR_TRAM_OWNER;
02501 td->owner[i] = tram_owner;
02502 }
02503 }
02504 }
02505 td->build_date = GetStationByTile(tile)->build_date;
02506
02507 const StationSpec *spec = GetStationSpec(tile);
02508
02509 if (spec != NULL) {
02510 td->station_class = GetStationClassName(spec->sclass);
02511 td->station_name = spec->name;
02512
02513 if (spec->grffile != NULL) {
02514 const GRFConfig *gc = GetGRFConfig(spec->grffile->grfid);
02515 td->grf = gc->name;
02516 }
02517 }
02518
02519 StringID str;
02520 switch (GetStationType(tile)) {
02521 default: NOT_REACHED();
02522 case STATION_RAIL: str = STR_305E_RAILROAD_STATION; break;
02523 case STATION_AIRPORT:
02524 str = (IsHangar(tile) ? STR_305F_AIRCRAFT_HANGAR : STR_3060_AIRPORT);
02525 break;
02526 case STATION_TRUCK: str = STR_3061_TRUCK_LOADING_AREA; break;
02527 case STATION_BUS: str = STR_3062_BUS_STATION; break;
02528 case STATION_OILRIG: str = STR_4807_OIL_RIG; break;
02529 case STATION_DOCK: str = STR_3063_SHIP_DOCK; break;
02530 case STATION_BUOY: str = STR_3069_BUOY; break;
02531 }
02532 td->str = str;
02533 }
02534
02535
02536 static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
02537 {
02538 TrackBits trackbits = TRACK_BIT_NONE;
02539
02540 switch (mode) {
02541 case TRANSPORT_RAIL:
02542 if (IsRailwayStation(tile) && !IsStationTileBlocked(tile)) {
02543 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
02544 }
02545 break;
02546
02547 case TRANSPORT_WATER:
02548
02549 if (IsBuoy(tile)) {
02550 trackbits = TRACK_BIT_ALL;
02551
02552 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
02553
02554 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
02555 }
02556 break;
02557
02558 case TRANSPORT_ROAD:
02559 if ((GetRoadTypes(tile) & sub_mode) != 0 && IsRoadStop(tile)) {
02560 DiagDirection dir = GetRoadStopDir(tile);
02561 Axis axis = DiagDirToAxis(dir);
02562
02563 if (side != INVALID_DIAGDIR) {
02564 if (axis != DiagDirToAxis(side) || (IsStandardRoadStopTile(tile) && dir != side)) break;
02565 }
02566
02567 trackbits = AxisToTrackBits(axis);
02568 }
02569 break;
02570
02571 default:
02572 break;
02573 }
02574
02575 return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), TRACKDIR_BIT_NONE);
02576 }
02577
02578
02579 static void TileLoop_Station(TileIndex tile)
02580 {
02581
02582
02583 switch (GetStationType(tile)) {
02584 case STATION_AIRPORT:
02585 switch (GetStationGfx(tile)) {
02586 case GFX_RADAR_LARGE_FIRST:
02587 case GFX_WINDSACK_FIRST :
02588 case GFX_RADAR_INTERNATIONAL_FIRST:
02589 case GFX_RADAR_METROPOLITAN_FIRST:
02590 case GFX_RADAR_DISTRICTWE_FIRST:
02591 case GFX_WINDSACK_INTERCON_FIRST :
02592 AddAnimatedTile(tile);
02593 break;
02594 }
02595 break;
02596
02597 case STATION_DOCK:
02598 if (GetTileSlope(tile, NULL) != SLOPE_FLAT) break;
02599
02600 case STATION_OILRIG:
02601 case STATION_BUOY:
02602 TileLoop_Water(tile);
02603 break;
02604
02605 default: break;
02606 }
02607 }
02608
02609
02610 static void AnimateTile_Station(TileIndex tile)
02611 {
02612 struct AnimData {
02613 StationGfx from;
02614 StationGfx to;
02615 byte delay;
02616 };
02617
02618 static const AnimData data[] = {
02619 { GFX_RADAR_LARGE_FIRST, GFX_RADAR_LARGE_LAST, 3 },
02620 { GFX_WINDSACK_FIRST, GFX_WINDSACK_LAST, 1 },
02621 { GFX_RADAR_INTERNATIONAL_FIRST, GFX_RADAR_INTERNATIONAL_LAST, 3 },
02622 { GFX_RADAR_METROPOLITAN_FIRST, GFX_RADAR_METROPOLITAN_LAST, 3 },
02623 { GFX_RADAR_DISTRICTWE_FIRST, GFX_RADAR_DISTRICTWE_LAST, 3 },
02624 { GFX_WINDSACK_INTERCON_FIRST, GFX_WINDSACK_INTERCON_LAST, 1 }
02625 };
02626
02627 if (IsRailwayStation(tile)) {
02628 AnimateStationTile(tile);
02629 return;
02630 }
02631
02632 StationGfx gfx = GetStationGfx(tile);
02633
02634 for (const AnimData *i = data; i != endof(data); i++) {
02635 if (i->from <= gfx && gfx <= i->to) {
02636 if ((_tick_counter & i->delay) == 0) {
02637 SetStationGfx(tile, gfx < i->to ? gfx + 1 : i->from);
02638 MarkTileDirtyByTile(tile);
02639 }
02640 break;
02641 }
02642 }
02643 }
02644
02645
02646 static bool ClickTile_Station(TileIndex tile)
02647 {
02648 if (IsHangar(tile)) {
02649 ShowDepotWindow(tile, VEH_AIRCRAFT);
02650 } else {
02651 ShowStationViewWindow(GetStationIndex(tile));
02652 }
02653 return true;
02654 }
02655
02656 static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
02657 {
02658 StationID station_id = GetStationIndex(tile);
02659
02660 if (v->type == VEH_TRAIN) {
02661 if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
02662 if (IsRailwayStation(tile) && IsFrontEngine(v) &&
02663 !IsCompatibleTrainStationTile(tile + TileOffsByDiagDir(DirToDiagDir(v->direction)), tile)) {
02664 DiagDirection dir = DirToDiagDir(v->direction);
02665
02666 x &= 0xF;
02667 y &= 0xF;
02668
02669 if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
02670 if (y == TILE_SIZE / 2) {
02671 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
02672 int stop = TILE_SIZE - (v->u.rail.cached_veh_length + 1) / 2;
02673 if (x == stop) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET);
02674 if (x < stop) {
02675 uint16 spd;
02676
02677 v->vehstatus |= VS_TRAIN_SLOWING;
02678 spd = max(0, (stop - x) * 20 - 15);
02679 if (spd < v->cur_speed) v->cur_speed = spd;
02680 }
02681 }
02682 }
02683 } else if (v->type == VEH_ROAD) {
02684 if (v->u.road.state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)v->u.road.state) && v->u.road.frame == 0) {
02685 if (IsRoadStop(tile) && IsRoadVehFront(v)) {
02686
02687 RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile));
02688
02689 if (IsDriveThroughStopTile(tile)) {
02690 if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
02691
02692
02693 byte side = ((DirToDiagDir(v->direction) == ReverseDiagDir(GetRoadStopDir(tile))) == (v->u.road.overtaking == 0)) ? 0 : 1;
02694
02695 if (!rs->IsFreeBay(side)) return VETSB_CANNOT_ENTER;
02696
02697
02698 if (GetRoadStopType(tile) == (IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? ROADSTOP_BUS : ROADSTOP_TRUCK) &&
02699 v->current_order.GetDestination() == GetStationIndex(tile)) {
02700 SetBit(v->u.road.state, RVS_IS_STOPPING);
02701 rs->AllocateDriveThroughBay(side);
02702 }
02703
02704
02705 if (side == 1) SetBit(v->u.road.state, RVS_USING_SECOND_BAY);
02706
02707 SetBit(v->u.road.state, RVS_IN_DT_ROAD_STOP);
02708 return VETSB_CONTINUE;
02709 }
02710
02711
02712
02713 if (rs->IsEntranceBusy() || !rs->HasFreeBay() || RoadVehHasArticPart(v)) return VETSB_CANNOT_ENTER;
02714
02715 SetBit(v->u.road.state, RVS_IN_ROAD_STOP);
02716
02717
02718 uint bay_nr = rs->AllocateBay();
02719 SB(v->u.road.state, RVS_USING_SECOND_BAY, 1, bay_nr);
02720
02721
02722 rs->SetEntranceBusy(true);
02723 }
02724 }
02725 }
02726
02727 return VETSB_CONTINUE;
02728 }
02729
02730
02731 static void StationHandleBigTick(Station *st)
02732 {
02733 UpdateStationAcceptance(st, true);
02734
02735 if (st->facilities == 0 && ++st->delete_ctr >= 8) delete st;
02736
02737 }
02738
02739 static inline void byte_inc_sat(byte *p)
02740 {
02741 byte b = *p + 1;
02742 if (b != 0) *p = b;
02743 }
02744
02745 static void UpdateStationRating(Station *st)
02746 {
02747 bool waiting_changed = false;
02748
02749 byte_inc_sat(&st->time_since_load);
02750 byte_inc_sat(&st->time_since_unload);
02751
02752 GoodsEntry *ge = st->goods;
02753 do {
02754
02755
02756
02757 if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP) && ge->rating < INITIAL_STATION_RATING) {
02758 ge->rating++;
02759 }
02760
02761
02762 if (HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) {
02763 byte_inc_sat(&ge->days_since_pickup);
02764
02765 int rating = 0;
02766
02767 {
02768 int b = ge->last_speed - 85;
02769 if (b >= 0)
02770 rating += b >> 2;
02771 }
02772
02773 {
02774 byte age = ge->last_age;
02775 (age >= 3) ||
02776 (rating += 10, age >= 2) ||
02777 (rating += 10, age >= 1) ||
02778 (rating += 13, true);
02779 }
02780
02781 if (IsValidCompanyID(st->owner) && HasBit(st->town->statues, st->owner)) rating += 26;
02782
02783 {
02784 byte days = ge->days_since_pickup;
02785 if (st->last_vehicle_type == VEH_SHIP) days >>= 2;
02786 (days > 21) ||
02787 (rating += 25, days > 12) ||
02788 (rating += 25, days > 6) ||
02789 (rating += 45, days > 3) ||
02790 (rating += 35, true);
02791 }
02792
02793 uint waiting = ge->cargo.Count();
02794 (rating -= 90, waiting > 1500) ||
02795 (rating += 55, waiting > 1000) ||
02796 (rating += 35, waiting > 600) ||
02797 (rating += 10, waiting > 300) ||
02798 (rating += 20, waiting > 100) ||
02799 (rating += 10, true);
02800
02801 {
02802 int or_ = ge->rating;
02803
02804
02805 ge->rating = rating = or_ + Clamp(Clamp(rating, 0, 255) - or_, -2, 2);
02806
02807
02808
02809 if (rating <= 64 && waiting >= 200) {
02810 int dec = Random() & 0x1F;
02811 if (waiting < 400) dec &= 7;
02812 waiting -= dec + 1;
02813 waiting_changed = true;
02814 }
02815
02816
02817 if (rating <= 127 && waiting != 0) {
02818 uint32 r = Random();
02819 if (rating <= (int)GB(r, 0, 7)) {
02820
02821 waiting = max((int)waiting - (int)GB(r, 8, 2) - 1, 0);
02822 waiting_changed = true;
02823 }
02824 }
02825
02826
02827
02828
02829 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
02830 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
02831 static const uint MAX_WAITING_CARGO = 1 << 15;
02832
02833 if (waiting > WAITING_CARGO_THRESHOLD) {
02834 uint difference = waiting - WAITING_CARGO_THRESHOLD;
02835 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
02836
02837 waiting = min(waiting, MAX_WAITING_CARGO);
02838 waiting_changed = true;
02839 }
02840
02841 if (waiting_changed) ge->cargo.Truncate(waiting);
02842 }
02843 }
02844 } while (++ge != endof(st->goods));
02845
02846 StationID index = st->index;
02847 if (waiting_changed) {
02848 InvalidateWindow(WC_STATION_VIEW, index);
02849 } else {
02850 InvalidateWindowWidget(WC_STATION_VIEW, index, SVW_RATINGLIST);
02851 }
02852 }
02853
02854
02855 static void StationHandleSmallTick(Station *st)
02856 {
02857 if (st->facilities == 0) return;
02858
02859 byte b = st->delete_ctr + 1;
02860 if (b >= 185) b = 0;
02861 st->delete_ctr = b;
02862
02863 if (b == 0) UpdateStationRating(st);
02864 }
02865
02866 void OnTick_Station()
02867 {
02868 if (_game_mode == GM_EDITOR) return;
02869
02870 uint i = _station_tick_ctr;
02871 if (++_station_tick_ctr > GetMaxStationIndex()) _station_tick_ctr = 0;
02872
02873 if (IsValidStationID(i)) StationHandleBigTick(GetStation(i));
02874
02875 Station *st;
02876 FOR_ALL_STATIONS(st) {
02877 StationHandleSmallTick(st);
02878
02879
02880
02881
02882 if ((_tick_counter + st->index) % 250 == 0) {
02883 StationAnimationTrigger(st, st->xy, STAT_ANIM_250_TICKS);
02884 }
02885 }
02886 }
02887
02888 void StationMonthlyLoop()
02889 {
02890
02891 }
02892
02893
02894 void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
02895 {
02896 Station *st;
02897
02898 FOR_ALL_STATIONS(st) {
02899 if (st->owner == owner &&
02900 DistanceManhattan(tile, st->xy) <= radius) {
02901 for (CargoID i = 0; i < NUM_CARGO; i++) {
02902 GoodsEntry *ge = &st->goods[i];
02903
02904 if (ge->acceptance_pickup != 0) {
02905 ge->rating = Clamp(ge->rating + amount, 0, 255);
02906 }
02907 }
02908 }
02909 }
02910 }
02911
02912 static void UpdateStationWaiting(Station *st, CargoID type, uint amount)
02913 {
02914 st->goods[type].cargo.Append(new CargoPacket(st->index, amount));
02915 SetBit(st->goods[type].acceptance_pickup, GoodsEntry::PICKUP);
02916
02917 StationAnimationTrigger(st, st->xy, STAT_ANIM_NEW_CARGO, type);
02918
02919 InvalidateWindow(WC_STATION_VIEW, st->index);
02920 st->MarkTilesDirty(true);
02921 }
02922
02923 static bool IsUniqueStationName(const char *name)
02924 {
02925 const Station *st;
02926
02927 FOR_ALL_STATIONS(st) {
02928 if (st->name != NULL && strcmp(st->name, name) == 0) return false;
02929 }
02930
02931 return true;
02932 }
02933
02940 CommandCost CmdRenameStation(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02941 {
02942 if (!IsValidStationID(p1)) return CMD_ERROR;
02943
02944 Station *st = GetStation(p1);
02945 if (!CheckOwnership(st->owner)) return CMD_ERROR;
02946
02947 bool reset = StrEmpty(text);
02948
02949 if (!reset) {
02950 if (strlen(text) >= MAX_LENGTH_STATION_NAME_BYTES) return CMD_ERROR;
02951 if (!IsUniqueStationName(text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
02952 }
02953
02954 if (flags & DC_EXEC) {
02955 free(st->name);
02956 st->name = reset ? NULL : strdup(text);
02957
02958 UpdateStationVirtCoord(st);
02959 InvalidateWindowData(WC_STATION_LIST, st->owner, 1);
02960 MarkWholeScreenDirty();
02961 }
02962
02963 return CommandCost();
02964 }
02965
02975 void FindStationsAroundTiles(TileIndex tile, int w_prod, int h_prod, StationList *stations)
02976 {
02977
02978 int max_rad = (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED);
02979
02980 for (int dy = -max_rad; dy < h_prod + max_rad; dy++) {
02981 for (int dx = -max_rad; dx < w_prod + max_rad; dx++) {
02982 TileIndex cur_tile = TileAddWrap(tile, dx, dy);
02983 if (cur_tile == INVALID_TILE || !IsTileType(cur_tile, MP_STATION)) continue;
02984
02985 Station *st = GetStationByTile(cur_tile);
02986
02987 if (st->IsBuoy()) continue;
02988
02989 if (_settings_game.station.modified_catchment) {
02990 int rad = st->GetCatchmentRadius();
02991 if (dx < -rad || dx >= rad + w_prod || dy < -rad || dy >= rad + h_prod) continue;
02992 }
02993
02994
02995
02996
02997 stations->Include(st);
02998 }
02999 }
03000 }
03001
03002 uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount)
03003 {
03004 Station *st1 = NULL;
03005 Station *st2 = NULL;
03006 uint best_rating1 = 0;
03007 uint best_rating2 = 0;
03008
03009 StationList all_stations;
03010 FindStationsAroundTiles(tile, w, h, &all_stations);
03011 for (Station **st_iter = all_stations.Begin(); st_iter != all_stations.End(); ++st_iter) {
03012 Station *st = *st_iter;
03013
03014
03015 if (st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) continue;
03016
03017 if (st->goods[type].rating == 0) continue;
03018
03019 if (_settings_game.order.selectgoods && st->goods[type].last_speed == 0) continue;
03020
03021 if (IsCargoInClass(type, CC_PASSENGERS)) {
03022 if (st->facilities == FACIL_TRUCK_STOP) continue;
03023 } else {
03024 if (st->facilities == FACIL_BUS_STOP) continue;
03025 }
03026
03027
03028 if (st1 == NULL || st->goods[type].rating >= best_rating1) {
03029 st2 = st1; best_rating2 = best_rating1; st1 = st; best_rating1 = st->goods[type].rating;
03030 } else if (st2 == NULL || st->goods[type].rating >= best_rating2) {
03031 st2 = st; best_rating2 = st->goods[type].rating;
03032 }
03033 }
03034
03035
03036 if (st1 == NULL) return 0;
03037
03038 if (st2 == NULL) {
03039
03040 uint moved = amount * best_rating1 / 256 + 1;
03041 UpdateStationWaiting(st1, type, moved);
03042 return moved;
03043 }
03044
03045
03046 assert(st1 != NULL);
03047 assert(st2 != NULL);
03048 assert(best_rating1 != 0 || best_rating2 != 0);
03049
03050
03051 best_rating2 >>= 1;
03052
03053
03054 uint t = (best_rating1 * (amount + 1)) / (best_rating1 + best_rating2);
03055
03056 uint moved = 0;
03057 if (t != 0) {
03058 moved = t * best_rating1 / 256 + 1;
03059 amount -= t;
03060 UpdateStationWaiting(st1, type, moved);
03061 }
03062
03063 if (amount != 0) {
03064 amount = amount * best_rating2 / 256 + 1;
03065 moved += amount;
03066 UpdateStationWaiting(st2, type, amount);
03067 }
03068
03069 return moved;
03070 }
03071
03072 void BuildOilRig(TileIndex tile)
03073 {
03074 if (!Station::CanAllocateItem()) {
03075 DEBUG(misc, 0, "Can't allocate station for oilrig at 0x%X, reverting to oilrig only", tile);
03076 return;
03077 }
03078
03079 Station *st = new Station(tile);
03080 st->town = ClosestTownFromTile(tile, UINT_MAX);
03081 st->sign.width_1 = 0;
03082
03083 st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
03084
03085 assert(IsTileType(tile, MP_INDUSTRY));
03086 MakeOilrig(tile, st->index, GetWaterClass(tile));
03087
03088 st->owner = OWNER_NONE;
03089 st->airport_flags = 0;
03090 st->airport_type = AT_OILRIG;
03091 st->xy = tile;
03092 st->bus_stops = NULL;
03093 st->truck_stops = NULL;
03094 st->airport_tile = tile;
03095 st->dock_tile = tile;
03096 st->train_tile = INVALID_TILE;
03097 st->had_vehicle_of_type = 0;
03098 st->time_since_load = 255;
03099 st->time_since_unload = 255;
03100 st->delete_ctr = 0;
03101 st->last_vehicle_type = VEH_INVALID;
03102 st->facilities = FACIL_AIRPORT | FACIL_DOCK;
03103 st->build_date = _date;
03104
03105 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
03106
03107 for (CargoID j = 0; j < NUM_CARGO; j++) {
03108 st->goods[j].acceptance_pickup = 0;
03109 st->goods[j].days_since_pickup = 255;
03110 st->goods[j].rating = INITIAL_STATION_RATING;
03111 st->goods[j].last_speed = 0;
03112 st->goods[j].last_age = 255;
03113 }
03114
03115 UpdateStationVirtCoordDirty(st);
03116 UpdateStationAcceptance(st, false);
03117 }
03118
03119 void DeleteOilRig(TileIndex tile)
03120 {
03121 Station *st = GetStationByTile(tile);
03122
03123 MakeWaterKeepingClass(tile, OWNER_NONE);
03124 MarkTileDirtyByTile(tile);
03125
03126 st->dock_tile = INVALID_TILE;
03127 st->airport_tile = INVALID_TILE;
03128 st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
03129 st->airport_flags = 0;
03130
03131 st->rect.AfterRemoveTile(st, tile);
03132
03133 UpdateStationVirtCoordDirty(st);
03134 if (st->facilities == 0) delete st;
03135 }
03136
03137 static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
03138 {
03139 if (IsDriveThroughStopTile(tile)) {
03140 for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
03141
03142 if (GetRoadOwner(tile, rt) == old_owner) {
03143 SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
03144 }
03145 }
03146 }
03147
03148 if (!IsTileOwner(tile, old_owner)) return;
03149
03150 if (new_owner != INVALID_OWNER) {
03151
03152 SetTileOwner(tile, new_owner);
03153 InvalidateWindowClassesData(WC_STATION_LIST, 0);
03154 } else {
03155 if (IsDriveThroughStopTile(tile)) {
03156
03157 DoCommand(tile, 0, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, DC_EXEC | DC_BANKRUPT, CMD_REMOVE_ROAD_STOP);
03158 assert(IsTileType(tile, MP_ROAD));
03159
03160 ChangeTileOwner(tile, old_owner, new_owner);
03161 } else {
03162 DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
03163
03164
03165
03166 if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
03167 }
03168 }
03169 }
03170
03179 static bool CanRemoveRoadWithStop(TileIndex tile, DoCommandFlag flags)
03180 {
03181 Owner road_owner = _current_company;
03182 Owner tram_owner = _current_company;
03183
03184 RoadTypes rts = GetRoadTypes(tile);
03185 if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
03186 if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
03187
03188 if ((road_owner != OWNER_TOWN && !CheckOwnership(road_owner)) || !CheckOwnership(tram_owner)) return false;
03189
03190 return road_owner != OWNER_TOWN || CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_ROAD), OWNER_TOWN, ROADTYPE_ROAD, flags);
03191 }
03192
03193 static CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
03194 {
03195 if (flags & DC_AUTO) {
03196 switch (GetStationType(tile)) {
03197 case STATION_RAIL: return_cmd_error(STR_300B_MUST_DEMOLISH_RAILROAD);
03198 case STATION_AIRPORT: return_cmd_error(STR_300E_MUST_DEMOLISH_AIRPORT_FIRST);
03199 case STATION_TRUCK: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_MUST_DEMOLISH_CARGO_TRAM_STATION : STR_3047_MUST_DEMOLISH_TRUCK_STATION);
03200 case STATION_BUS: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_MUST_DEMOLISH_PASSENGER_TRAM_STATION : STR_3046_MUST_DEMOLISH_BUS_STATION);
03201 case STATION_BUOY: return_cmd_error(STR_306A_BUOY_IN_THE_WAY);
03202 case STATION_DOCK: return_cmd_error(STR_304D_MUST_DEMOLISH_DOCK_FIRST);
03203 case STATION_OILRIG:
03204 SetDParam(0, STR_4807_OIL_RIG);
03205 return_cmd_error(STR_4800_IN_THE_WAY);
03206 }
03207 }
03208
03209 Station *st = GetStationByTile(tile);
03210
03211 switch (GetStationType(tile)) {
03212 case STATION_RAIL: return RemoveRailroadStation(st, tile, flags);
03213 case STATION_AIRPORT: return RemoveAirport(st, flags);
03214 case STATION_TRUCK:
03215 if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags))
03216 return_cmd_error(STR_3047_MUST_DEMOLISH_TRUCK_STATION);
03217 return RemoveRoadStop(st, flags, tile);
03218 case STATION_BUS:
03219 if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags))
03220 return_cmd_error(STR_3046_MUST_DEMOLISH_BUS_STATION);
03221 return RemoveRoadStop(st, flags, tile);
03222 case STATION_BUOY: return RemoveBuoy(st, flags);
03223 case STATION_DOCK: return RemoveDock(st, flags);
03224 default: break;
03225 }
03226
03227 return CMD_ERROR;
03228 }
03229
03230 void InitializeStations()
03231 {
03232
03233 _Station_pool.CleanPool();
03234 _Station_pool.AddBlockToPool();
03235
03236
03237 _RoadStop_pool.CleanPool();
03238 _RoadStop_pool.AddBlockToPool();
03239
03240 _station_tick_ctr = 0;
03241 }
03242
03243 static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
03244 {
03245 if (_settings_game.construction.build_on_slopes && AutoslopeEnabled()) {
03246
03247
03248
03249 if (!IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new))) {
03250 switch (GetStationType(tile)) {
03251 case STATION_RAIL: {
03252 DiagDirection direction = AxisToDiagDir(GetRailStationAxis(tile));
03253 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
03254 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
03255 return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform);
03256 }
03257
03258 case STATION_AIRPORT:
03259 return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform);
03260
03261 case STATION_TRUCK:
03262 case STATION_BUS: {
03263 DiagDirection direction = GetRoadStopDir(tile);
03264 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
03265 if (IsDriveThroughStopTile(tile)) {
03266 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
03267 }
03268 return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform);
03269 }
03270
03271 default: break;
03272 }
03273 }
03274 }
03275 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
03276 }
03277
03278
03279 extern const TileTypeProcs _tile_type_station_procs = {
03280 DrawTile_Station,
03281 GetSlopeZ_Station,
03282 ClearTile_Station,
03283 GetAcceptedCargo_Station,
03284 GetTileDesc_Station,
03285 GetTileTrackStatus_Station,
03286 ClickTile_Station,
03287 AnimateTile_Station,
03288 TileLoop_Station,
03289 ChangeTileOwner_Station,
03290 NULL,
03291 VehicleEnter_Station,
03292 GetFoundation_Station,
03293 TerraformTile_Station,
03294 };