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