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