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