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