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 "debug.h"
00011 #include "tile_cmd.h"
00012 #include "landscape.h"
00013 #include "station_map.h"
00014 #include "viewport_func.h"
00015 #include "command_func.h"
00016 #include "town.h"
00017 #include "news_func.h"
00018 #include "airport.h"
00019 #include "sprite.h"
00020 #include "train.h"
00021 #include "roadveh.h"
00022 #include "water_map.h"
00023 #include "industry_map.h"
00024 #include "newgrf_callbacks.h"
00025 #include "newgrf_station.h"
00026 #include "newgrf_commons.h"
00027 #include "yapf/yapf.h"
00028 #include "road_type.h"
00029 #include "road_internal.h"
00030 #include "cargotype.h"
00031 #include "variables.h"
00032 #include "autoslope.h"
00033 #include "transparency.h"
00034 #include "water.h"
00035 #include "station_gui.h"
00036 #include "strings_func.h"
00037 #include "functions.h"
00038 #include "window_func.h"
00039 #include "date_func.h"
00040 #include "vehicle_func.h"
00041 #include "string_func.h"
00042 #include "signal_func.h"
00043 #include "oldpool_func.h"
00044 #include "animated_tile_func.h"
00045 #include "elrail_func.h"
00046 #include "newgrf.h"
00047 #include "core/smallvec_type.hpp"
00048
00049 #include "table/sprites.h"
00050 #include "table/strings.h"
00051
00052 DEFINE_OLD_POOL_GENERIC(Station, Station)
00053 DEFINE_OLD_POOL_GENERIC(RoadStop, RoadStop)
00054
00055
00062 bool IsHangar(TileIndex t)
00063 {
00064 assert(IsTileType(t, MP_STATION));
00065
00066 const Station *st = GetStationByTile(t);
00067 const AirportFTAClass *apc = st->Airport();
00068
00069 for (uint i = 0; i < apc->nof_depots; i++) {
00070 if (st->airport_tile + ToTileIndexDiff(apc->airport_depots[i]) == t) return true;
00071 }
00072
00073 return false;
00074 }
00075
00076 RoadStop *GetRoadStopByTile(TileIndex tile, RoadStopType type)
00077 {
00078 const Station *st = GetStationByTile(tile);
00079
00080 for (RoadStop *rs = st->GetPrimaryRoadStop(type);; rs = rs->next) {
00081 if (rs->xy == tile) return rs;
00082 assert(rs->next != NULL);
00083 }
00084 }
00085
00086
00087 static uint GetNumRoadStopsInStation(const Station *st, RoadStopType type)
00088 {
00089 uint num = 0;
00090
00091 assert(st != NULL);
00092 for (const RoadStop *rs = st->GetPrimaryRoadStop(type); rs != NULL; rs = rs->next) {
00093 num++;
00094 }
00095
00096 return num;
00097 }
00098
00099
00100 #define CHECK_STATIONS_ERR ((Station*)-1)
00101
00102 static Station *GetStationAround(TileIndex tile, int w, int h, StationID closest_station)
00103 {
00104
00105 BEGIN_TILE_LOOP(tile_cur, w + 2, h + 2, tile - TileDiffXY(1, 1))
00106 if (IsTileType(tile_cur, MP_STATION)) {
00107 StationID t = GetStationIndex(tile_cur);
00108
00109 if (closest_station == INVALID_STATION) {
00110 closest_station = t;
00111 } else if (closest_station != t) {
00112 _error_message = STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING;
00113 return CHECK_STATIONS_ERR;
00114 }
00115 }
00116 END_TILE_LOOP(tile_cur, w + 2, h + 2, tile - TileDiffXY(1, 1))
00117 return (closest_station == INVALID_STATION) ? NULL : GetStation(closest_station);
00118 }
00119
00125 typedef bool (*CMSAMatcher)(TileIndex tile);
00126
00135 static int CountMapSquareAround(TileIndex tile, CMSAMatcher cmp)
00136 {
00137 int num = 0;
00138
00139 for (int dx = -3; dx <= 3; dx++) {
00140 for (int dy = -3; dy <= 3; dy++) {
00141 if (cmp(TILE_MASK(tile + TileDiffXY(dx, dy)))) num++;
00142 }
00143 }
00144
00145 return num;
00146 }
00147
00153 static bool CMSAMine(TileIndex tile)
00154 {
00155
00156 if (!IsTileType(tile, MP_INDUSTRY)) return false;
00157
00158 const Industry *ind = GetIndustryByTile(tile);
00159
00160
00161 if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_EXTRACTIVE) == 0) return false;
00162
00163 for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
00164
00165 if (ind->produced_cargo[i] != CT_INVALID && (GetCargo(ind->produced_cargo[i])->classes & CC_LIQUID) == 0) return true;
00166 }
00167
00168 return false;
00169 }
00170
00176 static bool CMSAWater(TileIndex tile)
00177 {
00178 return IsTileType(tile, MP_WATER) && IsWater(tile);
00179 }
00180
00186 static bool CMSATree(TileIndex tile)
00187 {
00188 return IsTileType(tile, MP_TREES);
00189 }
00190
00196 static bool CMSAForest(TileIndex tile)
00197 {
00198
00199 if (!IsTileType(tile, MP_INDUSTRY)) return false;
00200
00201 const Industry *ind = GetIndustryByTile(tile);
00202
00203
00204 if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_ORGANIC) == 0) return false;
00205
00206 for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
00207
00208 if (ind->produced_cargo[i] != CT_INVALID && GetCargo(ind->produced_cargo[i])->label == 'WOOD') return true;
00209 }
00210
00211 return false;
00212 }
00213
00214 #define M(x) ((x) - STR_SV_STNAME)
00215
00216 enum StationNaming {
00217 STATIONNAMING_RAIL = 0,
00218 STATIONNAMING_ROAD = 0,
00219 STATIONNAMING_AIRPORT,
00220 STATIONNAMING_OILRIG,
00221 STATIONNAMING_DOCK,
00222 STATIONNAMING_BUOY,
00223 STATIONNAMING_HELIPORT,
00224 };
00225
00227 struct StationNameInformation {
00228 uint32 free_names;
00229 bool *indtypes;
00230 };
00231
00240 static bool FindNearIndustryName(TileIndex tile, void *user_data)
00241 {
00242
00243 StationNameInformation *sni = (StationNameInformation*)user_data;
00244 if (!IsTileType(tile, MP_INDUSTRY)) return false;
00245
00246
00247 IndustryType indtype = GetIndustryType(tile);
00248 if (GetIndustrySpec(indtype)->station_name == STR_UNDEFINED) return false;
00249
00250
00251
00252 sni->free_names &= ~(1 << M(STR_SV_STNAME_OILFIELD) | 1 << M(STR_SV_STNAME_MINES));
00253 return !sni->indtypes[indtype];
00254 }
00255
00256 static StringID GenerateStationName(Station *st, TileIndex tile, int flag)
00257 {
00258 static const uint32 _gen_station_name_bits[] = {
00259 0,
00260 1 << M(STR_SV_STNAME_AIRPORT),
00261 1 << M(STR_SV_STNAME_OILFIELD),
00262 1 << M(STR_SV_STNAME_DOCKS),
00263 0x1FF << M(STR_SV_STNAME_BUOY_1),
00264 1 << M(STR_SV_STNAME_HELIPORT),
00265 };
00266
00267 const Town *t = st->town;
00268 uint32 free_names = UINT32_MAX;
00269
00270 bool indtypes[NUM_INDUSTRYTYPES];
00271 memset(indtypes, 0, sizeof(indtypes));
00272
00273 const Station *s;
00274 FOR_ALL_STATIONS(s) {
00275 if (s != st && s->town == t) {
00276 if (s->indtype != IT_INVALID) {
00277 indtypes[s->indtype] = true;
00278 continue;
00279 }
00280 uint str = M(s->string_id);
00281 if (str <= 0x20) {
00282 if (str == M(STR_SV_STNAME_FOREST)) {
00283 str = M(STR_SV_STNAME_WOODS);
00284 }
00285 ClrBit(free_names, str);
00286 }
00287 }
00288 }
00289
00290 if (flag != STATIONNAMING_BUOY) {
00291 TileIndex indtile = tile;
00292 StationNameInformation sni = { free_names, indtypes };
00293 if (CircularTileSearch(&indtile, 7, FindNearIndustryName, &sni)) {
00294
00295 IndustryType indtype = GetIndustryType(indtile);
00296 const IndustrySpec *indsp = GetIndustrySpec(indtype);
00297
00298 if (indsp->station_name != STR_NULL) {
00299 st->indtype = indtype;
00300 return STR_SV_STNAME_FALLBACK;
00301 }
00302 }
00303
00304
00305 free_names = sni.free_names;
00306 }
00307
00308
00309 uint32 tmp = free_names & _gen_station_name_bits[flag];
00310 if (tmp != 0) return STR_SV_STNAME + FindFirstBit(tmp);
00311
00312
00313 if (HasBit(free_names, M(STR_SV_STNAME_MINES))) {
00314 if (CountMapSquareAround(tile, CMSAMine) >= 2) {
00315 return STR_SV_STNAME_MINES;
00316 }
00317 }
00318
00319
00320 if (DistanceMax(tile, t->xy) < 8) {
00321 if (HasBit(free_names, M(STR_SV_STNAME))) return STR_SV_STNAME;
00322
00323 if (HasBit(free_names, M(STR_SV_STNAME_CENTRAL))) return STR_SV_STNAME_CENTRAL;
00324 }
00325
00326
00327 if (HasBit(free_names, M(STR_SV_STNAME_LAKESIDE)) &&
00328 DistanceFromEdge(tile) < 20 &&
00329 CountMapSquareAround(tile, CMSAWater) >= 5) {
00330 return STR_SV_STNAME_LAKESIDE;
00331 }
00332
00333
00334 if (HasBit(free_names, M(STR_SV_STNAME_WOODS)) && (
00335 CountMapSquareAround(tile, CMSATree) >= 8 ||
00336 CountMapSquareAround(tile, CMSAForest) >= 2)
00337 ) {
00338 return _settings_game.game_creation.landscape == LT_TROPIC ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
00339 }
00340
00341
00342 uint z = GetTileZ(tile);
00343 uint z2 = GetTileZ(t->xy);
00344 if (z < z2) {
00345 if (HasBit(free_names, M(STR_SV_STNAME_VALLEY))) return STR_SV_STNAME_VALLEY;
00346 } else if (z > z2) {
00347 if (HasBit(free_names, M(STR_SV_STNAME_HEIGHTS))) return STR_SV_STNAME_HEIGHTS;
00348 }
00349
00350
00351 static const int8 _direction_and_table[] = {
00352 ~( (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00353 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00354 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00355 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) ),
00356 };
00357
00358 free_names &= _direction_and_table[
00359 (TileX(tile) < TileX(t->xy)) +
00360 (TileY(tile) < TileY(t->xy)) * 2];
00361
00362 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));
00363 return (tmp == 0) ? STR_SV_STNAME_FALLBACK : (STR_SV_STNAME + FindFirstBit(tmp));
00364 }
00365 #undef M
00366
00372 static Station *GetClosestDeletedStation(TileIndex tile)
00373 {
00374 uint threshold = 8;
00375 Station *best_station = NULL;
00376 Station *st;
00377
00378 FOR_ALL_STATIONS(st) {
00379 if (st->facilities == 0 && st->owner == _current_company) {
00380 uint cur_dist = DistanceManhattan(tile, st->xy);
00381
00382 if (cur_dist < threshold) {
00383 threshold = cur_dist;
00384 best_station = st;
00385 }
00386 }
00387 }
00388
00389 return best_station;
00390 }
00391
00395 static void UpdateStationVirtCoord(Station *st)
00396 {
00397 Point pt = RemapCoords2(TileX(st->xy) * TILE_SIZE, TileY(st->xy) * TILE_SIZE);
00398
00399 pt.y -= 32;
00400 if (st->facilities & FACIL_AIRPORT && st->airport_type == AT_OILRIG) pt.y -= 16;
00401
00402 SetDParam(0, st->index);
00403 SetDParam(1, st->facilities);
00404 UpdateViewportSignPos(&st->sign, pt.x, pt.y, STR_305C_0);
00405 }
00406
00408 void UpdateAllStationVirtCoord()
00409 {
00410 Station *st;
00411
00412 FOR_ALL_STATIONS(st) {
00413 UpdateStationVirtCoord(st);
00414 }
00415 }
00416
00425 static void UpdateStationVirtCoordDirty(Station *st)
00426 {
00427 st->MarkDirty();
00428 UpdateStationVirtCoord(st);
00429 st->MarkDirty();
00430 }
00431
00436 static uint GetAcceptanceMask(const Station *st)
00437 {
00438 uint mask = 0;
00439
00440 for (CargoID i = 0; i < NUM_CARGO; i++) {
00441 if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) mask |= 1 << i;
00442 }
00443 return mask;
00444 }
00445
00449 static void ShowRejectOrAcceptNews(const Station *st, uint num_items, CargoID *cargo, StringID msg)
00450 {
00451 for (uint i = 0; i < num_items; i++) {
00452 SetDParam(i + 1, GetCargo(cargo[i])->name);
00453 }
00454
00455 SetDParam(0, st->index);
00456 AddNewsItem(msg, NS_ACCEPTANCE, st->xy, st->index);
00457 }
00458
00467 void GetProductionAroundTiles(AcceptedCargo produced, TileIndex tile,
00468 int w, int h, int rad)
00469 {
00470 memset(produced, 0, sizeof(AcceptedCargo));
00471
00472 int x = TileX(tile);
00473 int y = TileY(tile);
00474
00475
00476
00477 int x2 = min(x + w + rad, MapSizeX());
00478 int x1 = max(x - rad, 0);
00479
00480 int y2 = min(y + h + rad, MapSizeY());
00481 int y1 = max(y - rad, 0);
00482
00483 assert(x1 < x2);
00484 assert(y1 < y2);
00485 assert(w > 0);
00486 assert(h > 0);
00487
00488 for (int yc = y1; yc != y2; yc++) {
00489 for (int xc = x1; xc != x2; xc++) {
00490 TileIndex tile = TileXY(xc, yc);
00491
00492 if (!IsTileType(tile, MP_STATION)) {
00493 GetProducedCargoProc *gpc = _tile_type_procs[GetTileType(tile)]->get_produced_cargo_proc;
00494 if (gpc != NULL) {
00495 CargoID cargos[256];
00496 memset(cargos, CT_INVALID, sizeof(cargos));
00497
00498 gpc(tile, cargos);
00499 for (uint i = 0; i < lengthof(cargos); ++i) {
00500 if (cargos[i] != CT_INVALID) produced[cargos[i]]++;
00501 }
00502 }
00503 }
00504 }
00505 }
00506 }
00507
00516 void GetAcceptanceAroundTiles(AcceptedCargo accepts, TileIndex tile,
00517 int w, int h, int rad)
00518 {
00519 memset(accepts, 0, sizeof(AcceptedCargo));
00520
00521 int x = TileX(tile);
00522 int y = TileY(tile);
00523
00524
00525
00526 int x2 = min(x + w + rad, MapSizeX());
00527 int y2 = min(y + h + rad, MapSizeY());
00528 int x1 = max(x - rad, 0);
00529 int y1 = max(y - rad, 0);
00530
00531 assert(x1 < x2);
00532 assert(y1 < y2);
00533 assert(w > 0);
00534 assert(h > 0);
00535
00536 for (int yc = y1; yc != y2; yc++) {
00537 for (int xc = x1; xc != x2; xc++) {
00538 TileIndex tile = TileXY(xc, yc);
00539
00540 if (!IsTileType(tile, MP_STATION)) {
00541 AcceptedCargo ac;
00542
00543 GetAcceptedCargo(tile, ac);
00544 for (uint i = 0; i < lengthof(ac); ++i) accepts[i] += ac[i];
00545 }
00546 }
00547 }
00548 }
00549
00550 static inline void MergePoint(Rect *rect, TileIndex tile)
00551 {
00552 int x = TileX(tile);
00553 int y = TileY(tile);
00554
00555 if (rect->left > x) rect->left = x;
00556 if (rect->bottom > y) rect->bottom = y;
00557 if (rect->right < x) rect->right = x;
00558 if (rect->top < y) rect->top = y;
00559 }
00560
00565 static void UpdateStationAcceptance(Station *st, bool show_msg)
00566 {
00567
00568 if (st->IsBuoy()) return;
00569
00570 Rect rect;
00571 rect.left = MapSizeX();
00572 rect.bottom = MapSizeY();
00573 rect.right = 0;
00574 rect.top = 0;
00575
00576
00577 uint old_acc = GetAcceptanceMask(st);
00578
00579
00580 if (st->train_tile != INVALID_TILE) {
00581 MergePoint(&rect, st->train_tile);
00582 MergePoint(&rect, st->train_tile + TileDiffXY(st->trainst_w - 1, st->trainst_h - 1));
00583 }
00584
00585 if (st->airport_tile != INVALID_TILE) {
00586 const AirportFTAClass *afc = st->Airport();
00587
00588 MergePoint(&rect, st->airport_tile);
00589 MergePoint(&rect, st->airport_tile + TileDiffXY(afc->size_x - 1, afc->size_y - 1));
00590 }
00591
00592 if (st->dock_tile != INVALID_TILE) {
00593 MergePoint(&rect, st->dock_tile);
00594 if (IsDockTile(st->dock_tile)) {
00595 MergePoint(&rect, st->dock_tile + TileOffsByDiagDir(GetDockDirection(st->dock_tile)));
00596 }
00597 }
00598
00599 for (const RoadStop *rs = st->bus_stops; rs != NULL; rs = rs->next) {
00600 MergePoint(&rect, rs->xy);
00601 }
00602
00603 for (const RoadStop *rs = st->truck_stops; rs != NULL; rs = rs->next) {
00604 MergePoint(&rect, rs->xy);
00605 }
00606
00607
00608 AcceptedCargo accepts;
00609 assert((rect.right >= rect.left) == !st->rect.IsEmpty());
00610 if (rect.right >= rect.left) {
00611 assert(rect.left == st->rect.left);
00612 assert(rect.top == st->rect.bottom);
00613 assert(rect.right == st->rect.right);
00614 assert(rect.bottom == st->rect.top);
00615 GetAcceptanceAroundTiles(
00616 accepts,
00617 TileXY(rect.left, rect.bottom),
00618 rect.right - rect.left + 1,
00619 rect.top - rect.bottom + 1,
00620 st->GetCatchmentRadius()
00621 );
00622 } else {
00623 memset(accepts, 0, sizeof(accepts));
00624 }
00625
00626
00627 for (CargoID i = 0; i < NUM_CARGO; i++) {
00628 uint amt = min(accepts[i], 15);
00629
00630
00631 bool is_passengers = IsCargoInClass(i, CC_PASSENGERS);
00632 if ((!is_passengers && !(st->facilities & (byte)~FACIL_BUS_STOP)) ||
00633 (is_passengers && !(st->facilities & (byte)~FACIL_TRUCK_STOP))) {
00634 amt = 0;
00635 }
00636
00637 SB(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, amt >= 8);
00638 }
00639
00640
00641 uint new_acc = GetAcceptanceMask(st);
00642 if (old_acc == new_acc) return;
00643
00644
00645 if (show_msg && st->owner == _local_company && st->facilities) {
00646
00647
00648 static const StringID accept_msg[] = {
00649 STR_3040_NOW_ACCEPTS,
00650 STR_3041_NOW_ACCEPTS_AND,
00651 };
00652 static const StringID reject_msg[] = {
00653 STR_303E_NO_LONGER_ACCEPTS,
00654 STR_303F_NO_LONGER_ACCEPTS_OR,
00655 };
00656
00657
00658 CargoID accepts[2] = { CT_INVALID, CT_INVALID };
00659 CargoID rejects[2] = { CT_INVALID, CT_INVALID };
00660 uint num_acc = 0;
00661 uint num_rej = 0;
00662
00663
00664 for (CargoID i = 0; i < NUM_CARGO; i++) {
00665 if (HasBit(new_acc, i)) {
00666 if (!HasBit(old_acc, i) && num_acc < lengthof(accepts)) {
00667
00668 accepts[num_acc++] = i;
00669 }
00670 } else {
00671 if (HasBit(old_acc, i) && num_rej < lengthof(rejects)) {
00672
00673 rejects[num_rej++] = i;
00674 }
00675 }
00676 }
00677
00678
00679 if (num_acc > 0) ShowRejectOrAcceptNews(st, num_acc, accepts, accept_msg[num_acc - 1]);
00680 if (num_rej > 0) ShowRejectOrAcceptNews(st, num_rej, rejects, reject_msg[num_rej - 1]);
00681 }
00682
00683
00684 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_ACCEPTLIST);
00685 }
00686
00687 static void UpdateStationSignCoord(Station *st)
00688 {
00689 const StationRect *r = &st->rect;
00690
00691 if (r->IsEmpty()) return;
00692
00693
00694 st->xy = TileXY(ClampU(TileX(st->xy), r->left, r->right), ClampU(TileY(st->xy), r->top, r->bottom));
00695 UpdateStationVirtCoordDirty(st);
00696 }
00697
00703 static void DeleteStationIfEmpty(Station *st)
00704 {
00705 if (st->facilities == 0) {
00706 st->delete_ctr = 0;
00707 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
00708 }
00709
00710 UpdateStationSignCoord(st);
00711 }
00712
00713 static CommandCost ClearTile_Station(TileIndex tile, byte flags);
00714
00725 CommandCost CheckFlatLandBelow(TileIndex tile, uint w, uint h, uint flags, uint invalid_dirs, StationID *station, bool check_clear = true)
00726 {
00727 CommandCost cost(EXPENSES_CONSTRUCTION);
00728 int allowed_z = -1;
00729
00730 BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
00731 if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) {
00732 return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
00733 }
00734
00735 if (!EnsureNoVehicleOnGround(tile_cur)) return CMD_ERROR;
00736
00737 uint z;
00738 Slope tileh = GetTileSlope(tile_cur, &z);
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748 if (IsSteepSlope(tileh) ||
00749 ((_is_old_ai_company || !_settings_game.construction.build_on_slopes) && tileh != SLOPE_FLAT)) {
00750 return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
00751 }
00752
00753 int flat_z = z;
00754 if (tileh != SLOPE_FLAT) {
00755
00756
00757 if ((HasBit(invalid_dirs, DIAGDIR_NE) && !(tileh & SLOPE_NE)) ||
00758 (HasBit(invalid_dirs, DIAGDIR_SE) && !(tileh & SLOPE_SE)) ||
00759 (HasBit(invalid_dirs, DIAGDIR_SW) && !(tileh & SLOPE_SW)) ||
00760 (HasBit(invalid_dirs, DIAGDIR_NW) && !(tileh & SLOPE_NW))) {
00761 return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
00762 }
00763 cost.AddCost(_price.terraform);
00764 flat_z += TILE_HEIGHT;
00765 }
00766
00767
00768 if (allowed_z == -1) {
00769
00770 allowed_z = flat_z;
00771 } else if (allowed_z != flat_z) {
00772 return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
00773 }
00774
00775
00776
00777
00778 if (station != NULL && IsTileType(tile_cur, MP_STATION)) {
00779 if (!IsRailwayStation(tile_cur)) {
00780 return ClearTile_Station(tile_cur, DC_AUTO);
00781 } else {
00782 StationID st = GetStationIndex(tile_cur);
00783 if (*station == INVALID_STATION) {
00784 *station = st;
00785 } else if (*station != st) {
00786 return_cmd_error(STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING);
00787 }
00788 }
00789 } else if (check_clear) {
00790 CommandCost ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00791 if (CmdFailed(ret)) return ret;
00792 cost.AddCost(ret);
00793 }
00794 } END_TILE_LOOP(tile_cur, w, h, tile)
00795
00796 return cost;
00797 }
00798
00799 static bool CanExpandRailroadStation(const Station *st, uint *fin, Axis axis)
00800 {
00801 uint curw = st->trainst_w;
00802 uint curh = st->trainst_h;
00803 TileIndex tile = fin[0];
00804 uint w = fin[1];
00805 uint h = fin[2];
00806
00807 if (_settings_game.station.nonuniform_stations) {
00808
00809 int x = min(TileX(st->train_tile), TileX(tile));
00810 int y = min(TileY(st->train_tile), TileY(tile));
00811 curw = max(TileX(st->train_tile) + curw, TileX(tile) + w) - x;
00812 curh = max(TileY(st->train_tile) + curh, TileY(tile) + h) - y;
00813 tile = TileXY(x, y);
00814 } else {
00815
00816
00817 BEGIN_TILE_LOOP(t, st->trainst_w, st->trainst_h, st->train_tile)
00818 if (!st->TileBelongsToRailStation(t)) {
00819 _error_message = STR_NONUNIFORM_STATIONS_DISALLOWED;
00820 return false;
00821 }
00822 END_TILE_LOOP(t, st->trainst_w, st->trainst_h, st->train_tile)
00823
00824
00825 if (GetRailStationAxis(st->train_tile) != axis) {
00826 _error_message = STR_NONUNIFORM_STATIONS_DISALLOWED;
00827 return false;
00828 }
00829
00830
00831 if (curw == w && st->train_tile == tile + TileDiffXY(0, h)) {
00832
00833 curh += h;
00834 } else if (curw == w && st->train_tile == tile - TileDiffXY(0, curh)) {
00835
00836 tile -= TileDiffXY(0, curh);
00837 curh += h;
00838 } else if (curh == h && st->train_tile == tile + TileDiffXY(w, 0)) {
00839
00840 curw += w;
00841 } else if (curh == h && st->train_tile == tile - TileDiffXY(curw, 0)) {
00842
00843 tile -= TileDiffXY(curw, 0);
00844 curw += w;
00845 } else {
00846 _error_message = STR_NONUNIFORM_STATIONS_DISALLOWED;
00847 return false;
00848 }
00849 }
00850
00851 if (curw > _settings_game.station.station_spread || curh > _settings_game.station.station_spread) {
00852 _error_message = STR_306C_STATION_TOO_SPREAD_OUT;
00853 return false;
00854 }
00855
00856
00857
00858 fin[0] = tile;
00859 fin[1] = curw;
00860 fin[2] = curh;
00861 return true;
00862 }
00863
00864 static inline byte *CreateSingle(byte *layout, int n)
00865 {
00866 int i = n;
00867 do *layout++ = 0; while (--i);
00868 layout[((n - 1) >> 1) - n] = 2;
00869 return layout;
00870 }
00871
00872 static inline byte *CreateMulti(byte *layout, int n, byte b)
00873 {
00874 int i = n;
00875 do *layout++ = b; while (--i);
00876 if (n > 4) {
00877 layout[0 - n] = 0;
00878 layout[n - 1 - n] = 0;
00879 }
00880 return layout;
00881 }
00882
00883 static void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec)
00884 {
00885 if (statspec != NULL && statspec->lengths >= plat_len &&
00886 statspec->platforms[plat_len - 1] >= numtracks &&
00887 statspec->layouts[plat_len - 1][numtracks - 1]) {
00888
00889 memcpy(layout, statspec->layouts[plat_len - 1][numtracks - 1],
00890 plat_len * numtracks);
00891 return;
00892 }
00893
00894 if (plat_len == 1) {
00895 CreateSingle(layout, numtracks);
00896 } else {
00897 if (numtracks & 1) layout = CreateSingle(layout, plat_len);
00898 numtracks >>= 1;
00899
00900 while (--numtracks >= 0) {
00901 layout = CreateMulti(layout, plat_len, 4);
00902 layout = CreateMulti(layout, plat_len, 6);
00903 }
00904 }
00905 }
00906
00921 CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, uint32 p2, const char *text)
00922 {
00923
00924 if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile_org)) return CMD_ERROR;
00925 if (!ValParamRailtype((RailType)(p1 & 0xF))) return CMD_ERROR;
00926
00927
00928 Axis axis = Extract<Axis, 4>(p1);
00929 uint numtracks = GB(p1, 8, 8);
00930 uint plat_len = GB(p1, 16, 8);
00931
00932 int w_org, h_org;
00933 if (axis == AXIS_X) {
00934 w_org = plat_len;
00935 h_org = numtracks;
00936 } else {
00937 h_org = plat_len;
00938 w_org = numtracks;
00939 }
00940
00941 StationID station_to_join = GB(p2, 16, 16);
00942 bool distant_join = (station_to_join != INVALID_STATION);
00943
00944 if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR;
00945
00946 if (h_org > _settings_game.station.station_spread || w_org > _settings_game.station.station_spread) return CMD_ERROR;
00947
00948
00949 uint finalvalues[3];
00950 finalvalues[0] = tile_org;
00951 finalvalues[1] = w_org;
00952 finalvalues[2] = h_org;
00953
00954
00955 StationID est = INVALID_STATION;
00956
00957
00958
00959 CommandCost ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags & ~DC