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 byte old_specindex = IsTileType(tile, MP_STATION) ? GetCustomStationSpecIndex(tile) : 0;
01042 MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, (RailType)GB(p1, 0, 4));
01043
01044 DeallocateSpecFromStation(st, old_specindex);
01045
01046 SetCustomStationSpecIndex(tile, specindex);
01047 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
01048 SetStationAnimationFrame(tile, 0);
01049
01050 if (statspec != NULL) {
01051
01052 uint32 platinfo = GetPlatformInfo(AXIS_X, 0, plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
01053
01054
01055 uint16 callback = GetStationCallback(CBID_STATION_TILE_LAYOUT, platinfo, 0, statspec, NULL, tile);
01056 if (callback != CALLBACK_FAILED && callback < 8) SetStationGfx(tile, (callback & ~1) + axis);
01057
01058
01059 StationAnimationTrigger(st, tile, STAT_ANIM_BUILT);
01060 }
01061
01062 tile += tile_delta;
01063 } while (--w);
01064 AddTrackToSignalBuffer(tile_org, track, _current_company);
01065 YapfNotifyTrackLayoutChange(tile_org, track);
01066 tile_org += tile_delta ^ TileDiffXY(1, 1);
01067 } while (--numtracks);
01068
01069 for (uint i = 0; i < affected_vehicles.Length(); ++i) {
01070
01071 Vehicle *v = affected_vehicles[i];
01072 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(GetVehicleTrackdir(v)), true);
01073 TryPathReserve(v, true, true);
01074 for (; v->Next() != NULL; v = v->Next()) ;
01075 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(GetVehicleTrackdir(v))), true);
01076 }
01077
01078 st->MarkTilesDirty(false);
01079 UpdateStationVirtCoordDirty(st);
01080 UpdateStationAcceptance(st, false);
01081 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
01082 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
01083 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_TRAINS);
01084 }
01085
01086 return cost;
01087 }
01088
01089 static void MakeRailwayStationAreaSmaller(Station *st)
01090 {
01091 uint w = st->trainst_w;
01092 uint h = st->trainst_h;
01093 TileIndex tile = st->train_tile;
01094
01095 restart:
01096
01097
01098 if (w != 0 && h != 0) {
01099
01100 for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(0, i));) {
01101
01102 if (++i == h) {
01103 tile += TileDiffXY(1, 0);
01104 w--;
01105 goto restart;
01106 }
01107 }
01108
01109
01110 for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(w - 1, i));) {
01111
01112 if (++i == h) {
01113 w--;
01114 goto restart;
01115 }
01116 }
01117
01118
01119 for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(i, 0));) {
01120
01121 if (++i == w) {
01122 tile += TileDiffXY(0, 1);
01123 h--;
01124 goto restart;
01125 }
01126 }
01127
01128
01129 for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(i, h - 1));) {
01130
01131 if (++i == w) {
01132 h--;
01133 goto restart;
01134 }
01135 }
01136 } else {
01137 tile = INVALID_TILE;
01138 }
01139
01140 st->trainst_w = w;
01141 st->trainst_h = h;
01142 st->train_tile = tile;
01143 }
01144
01152 CommandCost CmdRemoveFromRailroadStation(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01153 {
01154 TileIndex start = p1 == 0 ? tile : p1;
01155
01156
01157 int quantity = 0;
01158
01159 if (tile >= MapSize() || start >= MapSize()) return CMD_ERROR;
01160
01161
01162 int ex = TileX(tile);
01163 int ey = TileY(tile);
01164 int sx = TileX(start);
01165 int sy = TileY(start);
01166 if (ex < sx) Swap(ex, sx);
01167 if (ey < sy) Swap(ey, sy);
01168 tile = TileXY(sx, sy);
01169
01170 int size_x = ex - sx + 1;
01171 int size_y = ey - sy + 1;
01172
01173
01174 BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) {
01175
01176 if (!IsTileType(tile2, MP_STATION) || !IsRailwayStation(tile2)) {
01177 continue;
01178 }
01179
01180
01181 if (!EnsureNoVehicleOnGround(tile2)) {
01182 continue;
01183 }
01184
01185
01186 Station *st = GetStationByTile(tile2);
01187 if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) {
01188 continue;
01189 }
01190
01191
01192
01193
01194 if (!_settings_game.station.nonuniform_stations) return_cmd_error(STR_NONUNIFORM_STATIONS_DISALLOWED);
01195
01196
01197 quantity++;
01198
01199 if (flags & DC_EXEC) {
01200
01201 uint specindex = GetCustomStationSpecIndex(tile2);
01202 Track track = GetRailStationTrack(tile2);
01203 Owner owner = GetTileOwner(tile2);
01204 Vehicle *v = NULL;
01205
01206 if (GetRailwayStationReservation(tile2)) {
01207 v = GetTrainForReservation(tile2, track);
01208 if (v != NULL) {
01209
01210 FreeTrainTrackReservation(v);
01211 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(GetVehicleTrackdir(v)), false);
01212 Vehicle *temp = v;
01213 for (; temp->Next() != NULL; temp = temp->Next()) ;
01214 if (IsRailwayStationTile(temp->tile)) SetRailwayStationPlatformReservation(temp->tile, TrackdirToExitdir(ReverseTrackdir(GetVehicleTrackdir(temp))), false);
01215 }
01216 }
01217
01218 DoClearSquare(tile2);
01219 st->rect.AfterRemoveTile(st, tile2);
01220 AddTrackToSignalBuffer(tile2, track, owner);
01221 YapfNotifyTrackLayoutChange(tile2, track);
01222
01223 DeallocateSpecFromStation(st, specindex);
01224
01225
01226
01227
01228 MakeRailwayStationAreaSmaller(st);
01229 st->MarkTilesDirty(false);
01230 UpdateStationSignCoord(st);
01231
01232 if (v != NULL) {
01233
01234 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(GetVehicleTrackdir(v)), true);
01235 TryPathReserve(v, true, true);
01236 for (; v->Next() != NULL; v = v->Next()) ;
01237 if (IsRailwayStationTile(v->tile)) SetRailwayStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(GetVehicleTrackdir(v))), true);
01238 }
01239
01240
01241 if (st->train_tile == INVALID_TILE) {
01242 st->facilities &= ~FACIL_TRAIN;
01243 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_TRAINS);
01244 UpdateStationVirtCoordDirty(st);
01245 DeleteStationIfEmpty(st);
01246 }
01247 }
01248 } END_TILE_LOOP(tile2, size_x, size_y, tile)
01249
01250
01251 if (quantity == 0) return CMD_ERROR;
01252
01253 return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_rail_station * quantity);
01254 }
01255
01256
01257 static CommandCost RemoveRailroadStation(Station *st, TileIndex tile, DoCommandFlag flags)
01258 {
01259
01260 if (_current_company == OWNER_WATER && _settings_game.station.nonuniform_stations) {
01261 return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAILROAD_STATION);
01262 }
01263
01264
01265 if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) return CMD_ERROR;
01266
01267
01268 tile = st->train_tile;
01269 int w = st->trainst_w;
01270 int h = st->trainst_h;
01271
01272 assert(w != 0 && h != 0);
01273
01274 CommandCost cost(EXPENSES_CONSTRUCTION);
01275
01276 do {
01277 int w_bak = w;
01278 do {
01279
01280 if (st->TileBelongsToRailStation(tile)) {
01281 if (!EnsureNoVehicleOnGround(tile))
01282 return CMD_ERROR;
01283 cost.AddCost(_price.remove_rail_station);
01284 if (flags & DC_EXEC) {
01285
01286 Track track = GetRailStationTrack(tile);
01287 Owner owner = GetTileOwner(tile);
01288 Vehicle *v = NULL;
01289 if (GetRailwayStationReservation(tile)) {
01290 v = GetTrainForReservation(tile, track);
01291 if (v != NULL) FreeTrainTrackReservation(v);
01292 }
01293 DoClearSquare(tile);
01294 AddTrackToSignalBuffer(tile, track, owner);
01295 YapfNotifyTrackLayoutChange(tile, track);
01296 if (v != NULL) TryPathReserve(v, true);
01297 }
01298 }
01299 tile += TileDiffXY(1, 0);
01300 } while (--w);
01301 w = w_bak;
01302 tile += TileDiffXY(-w, 1);
01303 } while (--h);
01304
01305 if (flags & DC_EXEC) {
01306 st->rect.AfterRemoveRect(st, st->train_tile, st->trainst_w, st->trainst_h);
01307
01308 st->train_tile = INVALID_TILE;
01309 st->trainst_w = st->trainst_h = 0;
01310 st->facilities &= ~FACIL_TRAIN;
01311
01312 free(st->speclist);
01313 st->num_specs = 0;
01314 st->speclist = NULL;
01315 st->cached_anim_triggers = 0;
01316
01317 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_TRAINS);
01318 UpdateStationVirtCoordDirty(st);
01319 DeleteStationIfEmpty(st);
01320 }
01321
01322 return cost;
01323 }
01324
01330 static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
01331 {
01332 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
01333
01334 if (*primary_stop == NULL) {
01335
01336 return primary_stop;
01337 } else {
01338
01339 RoadStop *stop = *primary_stop;
01340 while (stop->next != NULL) stop = stop->next;
01341 return &stop->next;
01342 }
01343 }
01344
01355 CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01356 {
01357 bool type = HasBit(p2, 0);
01358 bool is_drive_through = HasBit(p2, 1);
01359 bool build_over_road = is_drive_through && IsNormalRoadTile(tile);
01360 RoadTypes rts = (RoadTypes)GB(p2, 2, 2);
01361 StationID station_to_join = GB(p2, 16, 16);
01362 bool reuse = (station_to_join != NEW_STATION);
01363 if (!reuse) station_to_join = INVALID_STATION;
01364 bool distant_join = (station_to_join != INVALID_STATION);
01365 Owner tram_owner = _current_company;
01366 Owner road_owner = _current_company;
01367
01368 if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR;
01369
01370 if (!AreValidRoadTypes(rts) || !HasRoadTypesAvail(_current_company, rts)) return CMD_ERROR;
01371
01372
01373 if (!is_drive_through && HasBit(rts, ROADTYPE_TRAM)) return CMD_ERROR;
01374
01375
01376 if (!IsValidDiagDirection((DiagDirection)p1)) return CMD_ERROR;
01377
01378 if (is_drive_through && !IsValidAxis((Axis)p1)) return CMD_ERROR;
01379
01380 if (build_over_road && (GetAllRoadBits(tile) & ((Axis)p1 == AXIS_X ? ROAD_Y : ROAD_X)) != 0) return_cmd_error(STR_DRIVE_THROUGH_ERROR_DIRECTION);
01381
01382 if (!CheckIfAuthorityAllowsNewStation(tile, flags)) return CMD_ERROR;
01383
01384 RoadTypes cur_rts = IsNormalRoadTile(tile) ? GetRoadTypes(tile) : ROADTYPES_NONE;
01385 uint num_roadbits = 0;
01386
01387 if (build_over_road) {
01388
01389 if (HasBit(cur_rts, ROADTYPE_ROAD)) {
01390 road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
01391 if (road_owner == OWNER_TOWN) {
01392 if (!_settings_game.construction.road_stop_on_town_road) return_cmd_error(STR_DRIVE_THROUGH_ERROR_ON_TOWN_ROAD);
01393 } else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE && !CheckOwnership(road_owner)) {
01394 return CMD_ERROR;
01395 }
01396 num_roadbits += CountBits(GetRoadBits(tile, ROADTYPE_ROAD));
01397 }
01398
01399
01400 if (HasBit(cur_rts, ROADTYPE_TRAM)) {
01401 tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
01402 if (!_settings_game.construction.road_stop_on_competitor_road && tram_owner != OWNER_NONE && !CheckOwnership(tram_owner)) {
01403 return CMD_ERROR;
01404 }
01405 num_roadbits += CountBits(GetRoadBits(tile, ROADTYPE_TRAM));
01406 }
01407
01408
01409 if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
01410
01411
01412 rts |= cur_rts;
01413 }
01414
01415 CommandCost cost = CheckFlatLandBelow(tile, 1, 1, flags, is_drive_through ? 5 << p1 : 1 << p1, NULL, !build_over_road);
01416 if (CmdFailed(cost)) return cost;
01417 uint roadbits_to_build = CountBits(rts) * 2 - num_roadbits;
01418 cost.AddCost(_price.build_road * roadbits_to_build);
01419
01420 Station *st = NULL;
01421
01422 if (!_settings_game.station.adjacent_stations || !HasBit(p2, 5)) {
01423 st = GetStationAround(tile, 1, 1, INVALID_STATION);
01424 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
01425 }
01426
01427
01428 if (st == NULL && distant_join) st = GetStation(station_to_join);
01429
01430
01431 if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
01432
01433
01434 if (!RoadStop::CanAllocateItem()) return_cmd_error(type ? STR_TOO_MANY_TRUCK_STOPS : STR_TOO_MANY_BUS_STOPS);
01435
01436 if (st != NULL &&
01437 GetNumRoadStopsInStation(st, ROADSTOP_BUS) + GetNumRoadStopsInStation(st, ROADSTOP_TRUCK) >= RoadStop::LIMIT) {
01438 return_cmd_error(type ? STR_TOO_MANY_TRUCK_STOPS : STR_TOO_MANY_BUS_STOPS);
01439 }
01440
01441 if (st != NULL) {
01442 if (st->owner != _current_company) {
01443 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
01444 }
01445
01446 if (!st->rect.BeforeAddTile(tile, StationRect::ADD_TEST)) return CMD_ERROR;
01447 } else {
01448
01449 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
01450
01451 if (flags & DC_EXEC) {
01452 st = new Station(tile);
01453
01454 st->town = ClosestTownFromTile(tile, UINT_MAX);
01455 st->string_id = GenerateStationName(st, tile, STATIONNAMING_ROAD);
01456
01457 if (IsValidCompanyID(_current_company)) {
01458 SetBit(st->town->have_ratings, _current_company);
01459 }
01460 st->sign.width_1 = 0;
01461 }
01462 }
01463
01464 cost.AddCost((type) ? _price.build_truck_station : _price.build_bus_station);
01465
01466 if (flags & DC_EXEC) {
01467 RoadStop *road_stop = new RoadStop(tile);
01468
01469 RoadStop **currstop = FindRoadStopSpot(type, st);
01470 *currstop = road_stop;
01471
01472
01473 st->AddFacility((type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP, tile);
01474
01475 st->rect.BeforeAddTile(tile, StationRect::ADD_TRY);
01476
01477 RoadStopType rs_type = type ? ROADSTOP_TRUCK : ROADSTOP_BUS;
01478 if (is_drive_through) {
01479 MakeDriveThroughRoadStop(tile, st->owner, road_owner, tram_owner, st->index, rs_type, rts, (Axis)p1);
01480 } else {
01481 MakeRoadStop(tile, st->owner, st->index, rs_type, rts, (DiagDirection)p1);
01482 }
01483
01484 UpdateStationVirtCoordDirty(st);
01485 UpdateStationAcceptance(st, false);
01486 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
01487 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
01488 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_ROADVEHS);
01489 }
01490 return cost;
01491 }
01492
01493
01494 static Vehicle *ClearRoadStopStatusEnum(Vehicle *v, void *)
01495 {
01496 if (v->type == VEH_ROAD) ClrBit(v->u.road.state, RVS_IN_DT_ROAD_STOP);
01497
01498 return NULL;
01499 }
01500
01501
01508 static CommandCost RemoveRoadStop(Station *st, DoCommandFlag flags, TileIndex tile)
01509 {
01510 if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) {
01511 return CMD_ERROR;
01512 }
01513
01514 bool is_truck = IsTruckStop(tile);
01515
01516 RoadStop **primary_stop;
01517 RoadStop *cur_stop;
01518 if (is_truck) {
01519 primary_stop = &st->truck_stops;
01520 cur_stop = GetRoadStopByTile(tile, ROADSTOP_TRUCK);
01521 } else {
01522 primary_stop = &st->bus_stops;
01523 cur_stop = GetRoadStopByTile(tile, ROADSTOP_BUS);
01524 }
01525
01526 assert(cur_stop != NULL);
01527
01528
01529 if (IsDriveThroughStopTile(tile) && (flags & DC_BANKRUPT)) {
01530
01531 if (flags & DC_EXEC) FindVehicleOnPos(tile, NULL, &ClearRoadStopStatusEnum);
01532 } else {
01533 if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
01534 }
01535
01536 if (flags & DC_EXEC) {
01537 if (*primary_stop == cur_stop) {
01538
01539 *primary_stop = cur_stop->next;
01540
01541 if (*primary_stop == NULL) {
01542 st->facilities &= (is_truck ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP);
01543 }
01544 } else {
01545
01546 RoadStop *pred = *primary_stop;
01547 while (pred->next != cur_stop) pred = pred->next;
01548 pred->next = cur_stop->next;
01549 }
01550
01551 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_ROADVEHS);
01552 delete cur_stop;
01553
01554
01555 Vehicle *v;
01556 FOR_ALL_VEHICLES(v) {
01557 if (v->type == VEH_ROAD &&
01558 v->First() == v &&
01559 v->current_order.IsType(OT_GOTO_STATION) &&
01560 v->dest_tile == tile) {
01561 v->dest_tile = v->GetOrderStationLocation(st->index);
01562 }
01563 }
01564
01565 DoClearSquare(tile);
01566 st->rect.AfterRemoveTile(st, tile);
01567
01568 UpdateStationVirtCoordDirty(st);
01569 DeleteStationIfEmpty(st);
01570 }
01571
01572 return CommandCost(EXPENSES_CONSTRUCTION, (is_truck) ? _price.remove_truck_station : _price.remove_bus_station);
01573 }
01574
01581 CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01582 {
01583
01584 if (!IsTileType(tile, MP_STATION) || !IsRoadStop(tile) || (uint32)GetRoadStopType(tile) != GB(p2, 0, 1)) return CMD_ERROR;
01585 Station *st = GetStationByTile(tile);
01586
01587 bool is_drive_through = IsDriveThroughStopTile(tile);
01588 RoadTypes rts = GetRoadTypes(tile);
01589 RoadBits road_bits = IsDriveThroughStopTile(tile) ?
01590 ((GetRoadStopDir(tile) == DIAGDIR_NE) ? ROAD_X : ROAD_Y) :
01591 DiagDirToRoadBits(GetRoadStopDir(tile));
01592
01593 Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
01594 Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
01595 CommandCost ret = RemoveRoadStop(st, flags, tile);
01596
01597
01598 if ((flags & DC_EXEC) && CmdSucceeded(ret) && is_drive_through) {
01599
01600
01601
01602 MakeRoadNormal(tile, road_bits, rts, ClosestTownFromTile(tile, UINT_MAX)->index,
01603 road_owner, tram_owner);
01604 }
01605
01606 return ret;
01607 }
01608
01609
01610
01611
01612 static const byte _airport_sections_country[] = {
01613 54, 53, 52, 65,
01614 58, 57, 56, 55,
01615 64, 63, 63, 62
01616 };
01617
01618
01619 static const byte _airport_sections_town[] = {
01620 31, 9, 33, 9, 9, 32,
01621 27, 36, 29, 34, 8, 10,
01622 30, 11, 35, 13, 20, 21,
01623 51, 12, 14, 17, 19, 28,
01624 38, 13, 15, 16, 18, 39,
01625 26, 22, 23, 24, 25, 26
01626 };
01627
01628
01629 static const byte _airport_sections_metropolitan[] = {
01630 31, 9, 33, 9, 9, 32,
01631 27, 36, 29, 34, 8, 10,
01632 30, 11, 35, 13, 20, 21,
01633 102, 8, 8, 8, 8, 28,
01634 83, 84, 84, 84, 84, 83,
01635 26, 23, 23, 23, 23, 26
01636 };
01637
01638
01639 static const byte _airport_sections_international[] = {
01640 88, 89, 89, 89, 89, 89, 88,
01641 51, 8, 8, 8, 8, 8, 32,
01642 30, 8, 11, 27, 11, 8, 10,
01643 32, 8, 11, 27, 11, 8, 114,
01644 87, 8, 11, 85, 11, 8, 114,
01645 87, 8, 8, 8, 8, 8, 90,
01646 26, 23, 23, 23, 23, 23, 26
01647 };
01648
01649
01650 static const byte _airport_sections_intercontinental[] = {
01651 102, 120, 89, 89, 89, 89, 89, 89, 118,
01652 120, 23, 23, 23, 23, 23, 23, 119, 117,
01653 87, 54, 87, 8, 8, 8, 8, 51, 117,
01654 87, 162, 87, 85, 116, 116, 8, 9, 10,
01655 87, 8, 8, 11, 31, 11, 8, 160, 32,
01656 32, 160, 8, 11, 27, 11, 8, 8, 10,
01657 87, 8, 8, 11, 30, 11, 8, 8, 10,
01658 87, 142, 8, 11, 29, 11, 10, 163, 10,
01659 87, 164, 87, 8, 8, 8, 10, 37, 117,
01660 87, 120, 89, 89, 89, 89, 89, 89, 119,
01661 121, 23, 23, 23, 23, 23, 23, 119, 37
01662 };
01663
01664
01665
01666 static const byte _airport_sections_commuter[] = {
01667 85, 30, 115, 115, 32,
01668 87, 8, 8, 8, 10,
01669 87, 11, 11, 11, 10,
01670 26, 23, 23, 23, 26
01671 };
01672
01673
01674 static const byte _airport_sections_heliport[] = {
01675 66,
01676 };
01677
01678
01679 static const byte _airport_sections_helidepot[] = {
01680 124, 32,
01681 122, 123
01682 };
01683
01684
01685 static const byte _airport_sections_helistation[] = {
01686 32, 134, 159, 158,
01687 161, 142, 142, 157
01688 };
01689
01690 static const byte * const _airport_sections[] = {
01691 _airport_sections_country,
01692 _airport_sections_town,
01693 _airport_sections_heliport,
01694 _airport_sections_metropolitan,
01695 _airport_sections_international,
01696 _airport_sections_commuter,
01697 _airport_sections_helidepot,
01698 _airport_sections_intercontinental,
01699 _airport_sections_helistation
01700 };
01701
01709 static uint GetMinimalAirportDistanceToTile(const AirportFTAClass *afc, TileIndex town_tile, TileIndex airport_tile)
01710 {
01711 uint ttx = TileX(town_tile);
01712 uint tty = TileY(town_tile);
01713
01714 uint atx = TileX(airport_tile);
01715 uint aty = TileY(airport_tile);
01716
01717 uint btx = TileX(airport_tile) + afc->size_x - 1;
01718 uint bty = TileY(airport_tile) + afc->size_y - 1;
01719
01720
01721
01722
01723 uint dx = ttx < atx ? atx - ttx : (ttx <= btx ? 0 : ttx - btx);
01724 uint dy = tty < aty ? aty - tty : (tty <= bty ? 0 : tty - bty);
01725
01726 return dx + dy;
01727 }
01728
01737 uint8 GetAirportNoiseLevelForTown(const AirportFTAClass *afc, TileIndex town_tile, TileIndex tile)
01738 {
01739
01740
01741 if (afc->noise_level < 2) return afc->noise_level;
01742
01743 uint distance = GetMinimalAirportDistanceToTile(afc, town_tile, tile);
01744
01745
01746
01747
01748
01749 uint8 town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
01750
01751
01752
01753 uint noise_reduction = distance / town_tolerance_distance;
01754
01755
01756
01757 return noise_reduction >= afc->noise_level ? 1 : afc->noise_level - noise_reduction;
01758 }
01759
01767 Town *AirportGetNearestTown(const AirportFTAClass *afc, TileIndex airport_tile)
01768 {
01769 Town *t, *nearest = NULL;
01770 uint add = afc->size_x + afc->size_y - 2;
01771 uint mindist = UINT_MAX - add;
01772 FOR_ALL_TOWNS(t) {
01773 if (DistanceManhattan(t->xy, airport_tile) < mindist + add) {
01774 uint dist = GetMinimalAirportDistanceToTile(afc, t->xy, airport_tile);
01775 if (dist < mindist) {
01776 nearest = t;
01777 mindist = dist;
01778 }
01779 }
01780 }
01781
01782 return nearest;
01783 }
01784
01785
01787 void UpdateAirportsNoise()
01788 {
01789 Town *t;
01790 const Station *st;
01791
01792 FOR_ALL_TOWNS(t) t->noise_reached = 0;
01793
01794 FOR_ALL_STATIONS(st) {
01795 if (st->airport_tile != INVALID_TILE) {
01796 const AirportFTAClass *afc = GetAirport(st->airport_type);
01797 Town *nearest = AirportGetNearestTown(afc, st->airport_tile);
01798 nearest->noise_reached += GetAirportNoiseLevelForTown(afc, nearest->xy, st->airport_tile);
01799 }
01800 }
01801 }
01802
01803
01812 CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01813 {
01814 bool airport_upgrade = true;
01815 StationID station_to_join = GB(p2, 16, 16);
01816 bool reuse = (station_to_join != NEW_STATION);
01817 if (!reuse) station_to_join = INVALID_STATION;
01818 bool distant_join = (station_to_join != INVALID_STATION);
01819
01820 if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR;
01821
01822
01823 if (p1 > lengthof(_airport_sections) || !HasBit(GetValidAirports(), p1)) return CMD_ERROR;
01824
01825 if (!CheckIfAuthorityAllowsNewStation(tile, flags)) {
01826 return CMD_ERROR;
01827 }
01828
01829 Town *t = ClosestTownFromTile(tile, UINT_MAX);
01830 const AirportFTAClass *afc = GetAirport(p1);
01831 int w = afc->size_x;
01832 int h = afc->size_y;
01833 Station *st = NULL;
01834
01835 if (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread) {
01836 _error_message = STR_306C_STATION_TOO_SPREAD_OUT;
01837 return CMD_ERROR;
01838 }
01839
01840 CommandCost cost = CheckFlatLandBelow(tile, w, h, flags, 0, NULL);
01841 if (CmdFailed(cost)) return cost;
01842
01843
01844 Town *nearest = AirportGetNearestTown(afc, tile);
01845 uint newnoise_level = GetAirportNoiseLevelForTown(afc, nearest->xy, tile);
01846
01847
01848 StringID authority_refuse_message = STR_NULL;
01849
01850 if (_settings_game.economy.station_noise_level) {
01851
01852 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
01853 authority_refuse_message = STR_LOCAL_AUTHORITY_REFUSES_NOISE;
01854 }
01855 } else {
01856 uint num = 0;
01857 const Station *st;
01858 FOR_ALL_STATIONS(st) {
01859 if (st->town == t && st->facilities & FACIL_AIRPORT && st->airport_type != AT_OILRIG) num++;
01860 }
01861 if (num >= 2) {
01862 authority_refuse_message = STR_2035_LOCAL_AUTHORITY_REFUSES;
01863 }
01864 }
01865
01866 if (authority_refuse_message != STR_NULL) {
01867 SetDParam(0, t->index);
01868 return_cmd_error(authority_refuse_message);
01869 }
01870
01871 if (!_settings_game.station.adjacent_stations || !HasBit(p2, 0)) {
01872 st = GetStationAround(tile, w, h, INVALID_STATION);
01873 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
01874 } else {
01875 st = NULL;
01876 }
01877
01878
01879 if (st == NULL && distant_join) st = GetStation(station_to_join);
01880
01881
01882 if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
01883
01884 if (st != NULL) {
01885 if (st->owner != _current_company) {
01886 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
01887 }
01888
01889 if (!st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TEST)) return CMD_ERROR;
01890
01891 if (st->airport_tile != INVALID_TILE) {
01892 return_cmd_error(STR_300D_TOO_CLOSE_TO_ANOTHER_AIRPORT);
01893 }
01894 } else {
01895 airport_upgrade = false;
01896
01897
01898 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
01899
01900 if (flags & DC_EXEC) {
01901 st = new Station(tile);
01902
01903 st->town = t;
01904 st->string_id = GenerateStationName(st, tile, !(afc->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_HELIPORT : STATIONNAMING_AIRPORT);
01905
01906 if (IsValidCompanyID(_current_company)) {
01907 SetBit(st->town->have_ratings, _current_company);
01908 }
01909 st->sign.width_1 = 0;
01910 }
01911 }
01912
01913 cost.AddCost(_price.build_airport * w * h);
01914
01915 if (flags & DC_EXEC) {
01916
01917 nearest->noise_reached += newnoise_level;
01918
01919 st->airport_tile = tile;
01920 st->AddFacility(FACIL_AIRPORT, tile);
01921 st->airport_type = (byte)p1;
01922 st->airport_flags = 0;
01923
01924 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
01925
01926
01927
01928
01929
01930
01931
01932
01933 if (airport_upgrade) UpdateAirplanesOnNewStation(st);
01934
01935 {
01936 const byte *b = _airport_sections[p1];
01937
01938 BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
01939 MakeAirport(tile_cur, st->owner, st->index, *b - ((*b < 67) ? 8 : 24));
01940 b++;
01941 } END_TILE_LOOP(tile_cur, w, h, tile)
01942 }
01943
01944 UpdateStationVirtCoordDirty(st);
01945 UpdateStationAcceptance(st, false);
01946 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
01947 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
01948 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_PLANES);
01949
01950 if (_settings_game.economy.station_noise_level) {
01951 InvalidateWindow(WC_TOWN_VIEW, st->town->index);
01952 }
01953 }
01954
01955 return cost;
01956 }
01957
01958 static CommandCost RemoveAirport(Station *st, DoCommandFlag flags)
01959 {
01960 if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) {
01961 return CMD_ERROR;
01962 }
01963
01964 TileIndex tile = st->airport_tile;
01965
01966 const AirportFTAClass *afc = st->Airport();
01967 int w = afc->size_x;
01968 int h = afc->size_y;
01969
01970 CommandCost cost(EXPENSES_CONSTRUCTION, w * h * _price.remove_airport);
01971
01972 const Vehicle *v;
01973 FOR_ALL_VEHICLES(v) {
01974 if (!(v->type == VEH_AIRCRAFT && IsNormalAircraft(v))) continue;
01975
01976 if (v->u.air.targetairport == st->index && v->u.air.state != FLYING) return CMD_ERROR;
01977 }
01978
01979 BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
01980 if (!EnsureNoVehicleOnGround(tile_cur)) return CMD_ERROR;
01981
01982 if (flags & DC_EXEC) {
01983 DeleteAnimatedTile(tile_cur);
01984 DoClearSquare(tile_cur);
01985 }
01986 } END_TILE_LOOP(tile_cur, w, h, tile)
01987
01988 if (flags & DC_EXEC) {
01989 for (uint i = 0; i < afc->nof_depots; ++i) {
01990 DeleteWindowById(
01991 WC_VEHICLE_DEPOT, tile + ToTileIndexDiff(afc->airport_depots[i])
01992 );
01993 }
01994
01995
01996
01997
01998 Town *nearest = AirportGetNearestTown(afc, tile);
01999 nearest->noise_reached -= GetAirportNoiseLevelForTown(afc, nearest->xy, tile);
02000
02001 st->rect.AfterRemoveRect(st, tile, w, h);
02002
02003 st->airport_tile = INVALID_TILE;
02004 st->facilities &= ~FACIL_AIRPORT;
02005
02006 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_PLANES);
02007
02008 if (_settings_game.economy.station_noise_level) {
02009 InvalidateWindow(WC_TOWN_VIEW, st->town->index);
02010 }
02011
02012 UpdateStationVirtCoordDirty(st);
02013 DeleteStationIfEmpty(st);
02014 }
02015
02016 return cost;
02017 }
02018
02025 CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02026 {
02027 if (!IsWaterTile(tile) || tile == 0) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02028 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
02029
02030 if (GetTileSlope(tile, NULL) != SLOPE_FLAT) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02031
02032
02033 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
02034
02035 if (flags & DC_EXEC) {
02036 Station *st = new Station(tile);
02037
02038 st->town = ClosestTownFromTile(tile, UINT_MAX);
02039 st->string_id = GenerateStationName(st, tile, STATIONNAMING_BUOY);
02040
02041 if (IsValidCompanyID(_current_company)) {
02042 SetBit(st->town->have_ratings, _current_company);
02043 }
02044 st->sign.width_1 = 0;
02045 st->dock_tile = tile;
02046 st->facilities |= FACIL_DOCK;
02047
02048
02049 st->had_vehicle_of_type |= HVOT_BUOY;
02050 st->owner = OWNER_NONE;
02051
02052 st->build_date = _date;
02053
02054 MakeBuoy(tile, st->index, GetWaterClass(tile));
02055
02056 UpdateStationVirtCoordDirty(st);
02057 UpdateStationAcceptance(st, false);
02058 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
02059 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02060 }
02061
02062 return CommandCost(EXPENSES_CONSTRUCTION, _price.build_dock);
02063 }
02064
02071 bool HasStationInUse(StationID station, CompanyID company)
02072 {
02073 const Vehicle *v;
02074 FOR_ALL_VEHICLES(v) {
02075 if (company == INVALID_COMPANY || v->owner == company) {
02076 const Order *order;
02077 FOR_VEHICLE_ORDERS(v, order) {
02078 if (order->IsType(OT_GOTO_STATION) && order->GetDestination() == station) {
02079 return true;
02080 }
02081 }
02082 }
02083 }
02084 return false;
02085 }
02086
02087 static CommandCost RemoveBuoy(Station *st, DoCommandFlag flags)
02088 {
02089
02090 if (!IsValidCompanyID(_current_company)) return_cmd_error(INVALID_STRING_ID);
02091
02092 TileIndex tile = st->dock_tile;
02093
02094 if (HasStationInUse(st->index, INVALID_COMPANY)) return_cmd_error(STR_BUOY_IS_IN_USE);
02095
02096 if (!(flags & DC_BANKRUPT) && !EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
02097
02098 if (flags & DC_EXEC) {
02099 st->dock_tile = INVALID_TILE;
02100
02101
02102 st->facilities &= ~FACIL_DOCK;
02103 st->had_vehicle_of_type &= ~HVOT_BUOY;
02104
02105 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02106
02107
02108
02109
02110 MakeWaterKeepingClass(tile, GetTileOwner(tile));
02111 MarkTileDirtyByTile(tile);
02112
02113 UpdateStationVirtCoordDirty(st);
02114 DeleteStationIfEmpty(st);
02115 }
02116
02117 return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_truck_station);
02118 }
02119
02120 static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
02121 {-1, 0},
02122 { 0, 0},
02123 { 0, 0},
02124 { 0, -1}
02125 };
02126 static const byte _dock_w_chk[4] = { 2, 1, 2, 1 };
02127 static const byte _dock_h_chk[4] = { 1, 2, 1, 2 };
02128
02135 CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02136 {
02137 StationID station_to_join = GB(p2, 16, 16);
02138 bool reuse = (station_to_join != NEW_STATION);
02139 if (!reuse) station_to_join = INVALID_STATION;
02140 bool distant_join = (station_to_join != INVALID_STATION);
02141
02142 if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR;
02143
02144 DiagDirection direction = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
02145 if (direction == INVALID_DIAGDIR) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02146 direction = ReverseDiagDir(direction);
02147
02148
02149 if (IsWaterTile(tile)) return_cmd_error(STR_304B_SITE_UNSUITABLE);
02150
02151 if (!CheckIfAuthorityAllowsNewStation(tile, flags)) return CMD_ERROR;
02152
02153 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
02154
02155 if (CmdFailed(DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR))) return CMD_ERROR;
02156
02157 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
02158
02159 if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
02160 return_cmd_error(STR_304B_SITE_UNSUITABLE);
02161 }
02162
02163 if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
02164
02165
02166 WaterClass wc = GetWaterClass(tile_cur);
02167
02168 if (CmdFailed(DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR))) return CMD_ERROR;
02169
02170 tile_cur += TileOffsByDiagDir(direction);
02171 if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
02172 return_cmd_error(STR_304B_SITE_UNSUITABLE);
02173 }
02174
02175
02176 Station *st = NULL;
02177
02178 if (!_settings_game.station.adjacent_stations || !HasBit(p1, 0)) {
02179 st = GetStationAround(
02180 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02181 _dock_w_chk[direction], _dock_h_chk[direction], INVALID_STATION);
02182 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
02183 }
02184
02185
02186 if (st == NULL && distant_join) st = GetStation(station_to_join);
02187
02188
02189 if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
02190
02191 if (st != NULL) {
02192 if (st->owner != _current_company) {
02193 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
02194 }
02195
02196 if (!st->rect.BeforeAddRect(
02197 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02198 _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TEST)) return CMD_ERROR;
02199
02200 if (st->dock_tile != INVALID_TILE) return_cmd_error(STR_304C_TOO_CLOSE_TO_ANOTHER_DOCK);
02201 } else {
02202
02203 if (!Station::CanAllocateItem()) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
02204
02205 if (flags & DC_EXEC) {
02206 st = new Station(tile);
02207
02208 st->town = ClosestTownFromTile(tile, UINT_MAX);
02209 st->string_id = GenerateStationName(st, tile, STATIONNAMING_DOCK);
02210
02211 if (IsValidCompanyID(_current_company)) {
02212 SetBit(st->town->have_ratings, _current_company);
02213 }
02214 }
02215 }
02216
02217 if (flags & DC_EXEC) {
02218 st->dock_tile = tile;
02219 st->AddFacility(FACIL_DOCK, tile);
02220
02221 st->rect.BeforeAddRect(
02222 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02223 _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TRY);
02224
02225 MakeDock(tile, st->owner, st->index, direction, wc);
02226
02227 UpdateStationVirtCoordDirty(st);
02228 UpdateStationAcceptance(st, false);
02229 InvalidateWindowData(WC_SELECT_STATION, 0, 0);
02230 InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
02231 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02232 }
02233
02234 return CommandCost(EXPENSES_CONSTRUCTION, _price.build_dock);
02235 }
02236
02237 static CommandCost RemoveDock(Station *st, DoCommandFlag flags)
02238 {
02239 if (!CheckOwnership(st->owner)) return CMD_ERROR;
02240
02241 TileIndex tile1 = st->dock_tile;
02242 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
02243
02244 if (!EnsureNoVehicleOnGround(tile1)) return CMD_ERROR;
02245 if (!EnsureNoVehicleOnGround(tile2)) return CMD_ERROR;
02246
02247 if (flags & DC_EXEC) {
02248 DoClearSquare(tile1);
02249 MakeWaterKeepingClass(tile2, st->owner);
02250
02251 st->rect.AfterRemoveTile(st, tile1);
02252 st->rect.AfterRemoveTile(st, tile2);
02253
02254 MarkTileDirtyByTile(tile2);
02255
02256 st->dock_tile = INVALID_TILE;
02257 st->facilities &= ~FACIL_DOCK;
02258
02259 InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
02260 UpdateStationVirtCoordDirty(st);
02261 DeleteStationIfEmpty(st);
02262 }
02263
02264 return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_dock);
02265 }
02266
02267 #include "table/station_land.h"
02268
02269 const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx)
02270 {
02271 return &_station_display_datas[st][gfx];
02272 }
02273
02274 static void DrawTile_Station(TileInfo *ti)
02275 {
02276 const DrawTileSprites *t = NULL;
02277 RoadTypes roadtypes;
02278 int32 total_offset;
02279 int32 custom_ground_offset;
02280
02281 if (IsRailwayStation(ti->tile)) {
02282 const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
02283 roadtypes = ROADTYPES_NONE;
02284 total_offset = rti->total_offset;
02285 custom_ground_offset = rti->custom_ground_offset;
02286 } else {
02287 roadtypes = IsRoadStop(ti->tile) ? GetRoadTypes(ti->tile) : ROADTYPES_NONE;
02288 total_offset = 0;
02289 custom_ground_offset = 0;
02290 }
02291 uint32 relocation = 0;
02292 const Station *st = NULL;
02293 const StationSpec *statspec = NULL;
02294 Owner owner = GetTileOwner(ti->tile);
02295
02296 SpriteID palette;
02297 if (IsValidCompanyID(owner)) {
02298 palette = COMPANY_SPRITE_COLOUR(owner);
02299 } else {
02300
02301 palette = PALETTE_TO_GREY;
02302 }
02303
02304
02305 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile))
02306 DrawFoundation(ti, FOUNDATION_LEVELED);
02307
02308 if (IsCustomStationSpecIndex(ti->tile)) {
02309
02310 st = GetStationByTile(ti->tile);
02311 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
02312
02313 if (statspec != NULL) {
02314 uint tile = GetStationGfx(ti->tile);
02315
02316 relocation = GetCustomStationRelocation(statspec, st, ti->tile);
02317
02318 if (HasBit(statspec->callbackmask, CBM_STATION_SPRITE_LAYOUT)) {
02319 uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile);
02320 if (callback != CALLBACK_FAILED) tile = (callback & ~1) + GetRailStationAxis(ti->tile);
02321 }
02322
02323
02324 if (statspec->renderdata != NULL) {
02325 t = &statspec->renderdata[tile < statspec->tiles ? tile : (uint)GetRailStationAxis(ti->tile)];
02326 }
02327 }
02328 }
02329
02330 if (t == NULL || t->seq == NULL) t = &_station_display_datas[GetStationType(ti->tile)][GetStationGfx(ti->tile)];
02331
02332
02333 if (IsBuoy(ti->tile) || IsDock(ti->tile) || (IsOilRig(ti->tile) && GetWaterClass(ti->tile) != WATER_CLASS_INVALID)) {
02334 if (ti->tileh == SLOPE_FLAT) {
02335 DrawWaterClassGround(ti);
02336 } else {
02337 assert(IsDock(ti->tile));
02338 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
02339 WaterClass wc = GetWaterClass(water_tile);
02340 if (wc == WATER_CLASS_SEA) {
02341 DrawShoreTile(ti->tileh);
02342 } else {
02343 DrawClearLandTile(ti, 3);
02344 }
02345 }
02346 } else {
02347 SpriteID image = t->ground.sprite;
02348 SpriteID pal = t->ground.pal;
02349 if (HasBit(image, SPRITE_MODIFIER_USE_OFFSET)) {
02350 image += GetCustomStationGroundRelocation(statspec, st, ti->tile);
02351 image += custom_ground_offset;
02352 } else {
02353 image += total_offset;
02354 }
02355 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
02356
02357
02358 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && IsRailwayStation(ti->tile) && GetRailwayStationReservation(ti->tile)) {
02359 const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
02360 DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH);
02361 }
02362 }
02363
02364 if (IsRailwayStation(ti->tile) && HasCatenaryDrawn(GetRailType(ti->tile)) && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
02365
02366 if (HasBit(roadtypes, ROADTYPE_TRAM)) {
02367 Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
02368 DrawGroundSprite((HasBit(roadtypes, ROADTYPE_ROAD) ? SPR_TRAMWAY_OVERLAY : SPR_TRAMWAY_TRAM) + (axis ^ 1), PAL_NONE);
02369 DrawTramCatenary(ti, axis == AXIS_X ? ROAD_X : ROAD_Y);
02370 }
02371
02372 const DrawTileSeqStruct *dtss;
02373 foreach_draw_tile_seq(dtss, t->seq) {
02374 SpriteID image = dtss->image.sprite;
02375
02376
02377 if (IsInvisibilitySet(TO_BUILDINGS) && !HasBit(image, SPRITE_MODIFIER_OPAQUE)) return;
02378
02379 if (relocation == 0 || HasBit(image, SPRITE_MODIFIER_USE_OFFSET)) {
02380 image += total_offset;
02381 } else {
02382 image += relocation;
02383 }
02384
02385 SpriteID pal = SpriteLayoutPaletteTransform(image, dtss->image.pal, palette);
02386
02387 if ((byte)dtss->delta_z != 0x80) {
02388 AddSortableSpriteToDraw(
02389 image, pal,
02390 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
02391 dtss->size_x, dtss->size_y,
02392 dtss->size_z, ti->z + dtss->delta_z,
02393 !HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_BUILDINGS)
02394 );
02395 } else {
02396
02397 AddChildSpriteScreen(image, pal, dtss->delta_x, dtss->delta_y, !HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_BUILDINGS));
02398 }
02399 }
02400 }
02401
02402 void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
02403 {
02404 int32 total_offset = 0;
02405 SpriteID pal = COMPANY_SPRITE_COLOUR(_local_company);
02406 const DrawTileSprites *t = &_station_display_datas[st][image];
02407
02408 if (railtype != INVALID_RAILTYPE) {
02409 const RailtypeInfo *rti = GetRailTypeInfo(railtype);
02410 total_offset = rti->total_offset;
02411 }
02412
02413 SpriteID img = t->ground.sprite;
02414 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
02415
02416 if (roadtype == ROADTYPE_TRAM) {
02417 DrawSprite(SPR_TRAMWAY_TRAM + (t->ground.sprite == SPR_ROAD_PAVED_STRAIGHT_X ? 1 : 0), PAL_NONE, x, y);
02418 }
02419
02420 const DrawTileSeqStruct *dtss;
02421 foreach_draw_tile_seq(dtss, t->seq) {
02422 Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z);
02423 DrawSprite(dtss->image.sprite + total_offset, pal, x + pt.x, y + pt.y);
02424 }
02425 }
02426
02427 static uint GetSlopeZ_Station(TileIndex tile, uint x, uint y)
02428 {
02429 return GetTileMaxZ(tile);
02430 }
02431
02432 static Foundation GetFoundation_Station(TileIndex tile, Slope tileh)
02433 {
02434 return FlatteningFoundation(tileh);
02435 }
02436
02437 static void GetAcceptedCargo_Station(TileIndex tile, AcceptedCargo ac)
02438 {
02439
02440 }
02441
02442 static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
02443 {
02444 td->owner[0] = GetTileOwner(tile);
02445 if (IsDriveThroughStopTile(tile)) {
02446 Owner road_owner = INVALID_OWNER;
02447 Owner tram_owner = INVALID_OWNER;
02448 RoadTypes rts = GetRoadTypes(tile);
02449 if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
02450 if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
02451
02452
02453 if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) ||
02454 (road_owner != INVALID_OWNER && road_owner != td->owner[0])) {
02455 uint i = 1;
02456 if (road_owner != INVALID_OWNER) {
02457 td->owner_type[i] = STR_ROAD_OWNER;
02458 td->owner[i] = road_owner;
02459 i++;
02460 }
02461 if (tram_owner != INVALID_OWNER) {
02462 td->owner_type[i] = STR_TRAM_OWNER;
02463 td->owner[i] = tram_owner;
02464 }
02465 }
02466 }
02467 td->build_date = GetStationByTile(tile)->build_date;
02468
02469 const StationSpec *spec = GetStationSpec(tile);
02470
02471 if (spec != NULL) {
02472 td->station_class = GetStationClassName(spec->sclass);
02473 td->station_name = spec->name;
02474
02475 if (spec->grffile != NULL) {
02476 const GRFConfig *gc = GetGRFConfig(spec->grffile->grfid);
02477 td->grf = gc->name;
02478 }
02479 }
02480
02481 StringID str;
02482 switch (GetStationType(tile)) {
02483 default: NOT_REACHED();
02484 case STATION_RAIL: str = STR_305E_RAILROAD_STATION; break;
02485 case STATION_AIRPORT:
02486 str = (IsHangar(tile) ? STR_305F_AIRCRAFT_HANGAR : STR_3060_AIRPORT);
02487 break;
02488 case STATION_TRUCK: str = STR_3061_TRUCK_LOADING_AREA; break;
02489 case STATION_BUS: str = STR_3062_BUS_STATION; break;
02490 case STATION_OILRIG: str = STR_4807_OIL_RIG; break;
02491 case STATION_DOCK: str = STR_3063_SHIP_DOCK; break;
02492 case STATION_BUOY: str = STR_3069_BUOY; break;
02493 }
02494 td->str = str;
02495 }
02496
02497
02498 static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
02499 {
02500 TrackBits trackbits = TRACK_BIT_NONE;
02501
02502 switch (mode) {
02503 case TRANSPORT_RAIL:
02504 if (IsRailwayStation(tile) && !IsStationTileBlocked(tile)) {
02505 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
02506 }
02507 break;
02508
02509 case TRANSPORT_WATER:
02510
02511 if (IsBuoy(tile)) {
02512 trackbits = TRACK_BIT_ALL;
02513
02514 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
02515
02516 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
02517 }
02518 break;
02519
02520 case TRANSPORT_ROAD:
02521 if ((GetRoadTypes(tile) & sub_mode) != 0 && IsRoadStop(tile)) {
02522 DiagDirection dir = GetRoadStopDir(tile);
02523 Axis axis = DiagDirToAxis(dir);
02524
02525 if (side != INVALID_DIAGDIR) {
02526 if (axis != DiagDirToAxis(side) || (IsStandardRoadStopTile(tile) && dir != side)) break;
02527 }
02528
02529 trackbits = AxisToTrackBits(axis);
02530 }
02531 break;
02532
02533 default:
02534 break;
02535 }
02536
02537 return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), TRACKDIR_BIT_NONE);
02538 }
02539
02540
02541 static void TileLoop_Station(TileIndex tile)
02542 {
02543
02544
02545 switch (GetStationType(tile)) {
02546 case STATION_AIRPORT:
02547 switch (GetStationGfx(tile)) {
02548 case GFX_RADAR_LARGE_FIRST:
02549 case GFX_WINDSACK_FIRST :
02550 case GFX_RADAR_INTERNATIONAL_FIRST:
02551 case GFX_RADAR_METROPOLITAN_FIRST:
02552 case GFX_RADAR_DISTRICTWE_FIRST:
02553 case GFX_WINDSACK_INTERCON_FIRST :
02554 AddAnimatedTile(tile);
02555 break;
02556 }
02557 break;
02558
02559 case STATION_DOCK:
02560 if (GetTileSlope(tile, NULL) != SLOPE_FLAT) break;
02561
02562 case STATION_OILRIG:
02563 case STATION_BUOY:
02564 TileLoop_Water(tile);
02565 break;
02566
02567 default: break;
02568 }
02569 }
02570
02571
02572 static void AnimateTile_Station(TileIndex tile)
02573 {
02574 struct AnimData {
02575 StationGfx from;
02576 StationGfx to;
02577 byte delay;
02578 };
02579
02580 static const AnimData data[] = {
02581 { GFX_RADAR_LARGE_FIRST, GFX_RADAR_LARGE_LAST, 3 },
02582 { GFX_WINDSACK_FIRST, GFX_WINDSACK_LAST, 1 },
02583 { GFX_RADAR_INTERNATIONAL_FIRST, GFX_RADAR_INTERNATIONAL_LAST, 3 },
02584 { GFX_RADAR_METROPOLITAN_FIRST, GFX_RADAR_METROPOLITAN_LAST, 3 },
02585 { GFX_RADAR_DISTRICTWE_FIRST, GFX_RADAR_DISTRICTWE_LAST, 3 },
02586 { GFX_WINDSACK_INTERCON_FIRST, GFX_WINDSACK_INTERCON_LAST, 1 }
02587 };
02588
02589 if (IsRailwayStation(tile)) {
02590 AnimateStationTile(tile);
02591 return;
02592 }
02593
02594 StationGfx gfx = GetStationGfx(tile);
02595
02596 for (const AnimData *i = data; i != endof(data); i++) {
02597 if (i->from <= gfx && gfx <= i->to) {
02598 if ((_tick_counter & i->delay) == 0) {
02599 SetStationGfx(tile, gfx < i->to ? gfx + 1 : i->from);
02600 MarkTileDirtyByTile(tile);
02601 }
02602 break;
02603 }
02604 }
02605 }
02606
02607
02608 static bool ClickTile_Station(TileIndex tile)
02609 {
02610 if (IsHangar(tile)) {
02611 ShowDepotWindow(tile, VEH_AIRCRAFT);
02612 } else {
02613 ShowStationViewWindow(GetStationIndex(tile));
02614 }
02615 return true;
02616 }
02617
02618 static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
02619 {
02620 StationID station_id = GetStationIndex(tile);
02621
02622 if (v->type == VEH_TRAIN) {
02623 if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
02624 if (IsRailwayStation(tile) && IsFrontEngine(v) &&
02625 !IsCompatibleTrainStationTile(tile + TileOffsByDiagDir(DirToDiagDir(v->direction)), tile)) {
02626 DiagDirection dir = DirToDiagDir(v->direction);
02627
02628 x &= 0xF;
02629 y &= 0xF;
02630
02631 if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
02632 if (y == TILE_SIZE / 2) {
02633 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
02634 int stop = TILE_SIZE - (v->u.rail.cached_veh_length + 1) / 2;
02635 if (x == stop) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET);
02636 if (x < stop) {
02637 uint16 spd;
02638
02639 v->vehstatus |= VS_TRAIN_SLOWING;
02640 spd = max(0, (stop - x) * 20 - 15);
02641 if (spd < v->cur_speed) v->cur_speed = spd;
02642 }
02643 }
02644 }
02645 } else if (v->type == VEH_ROAD) {
02646 if (v->u.road.state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)v->u.road.state) && v->u.road.frame == 0) {
02647 if (IsRoadStop(tile) && IsRoadVehFront(v)) {
02648
02649 RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile));
02650
02651 if (IsDriveThroughStopTile(tile)) {
02652 if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
02653
02654
02655 byte side = ((DirToDiagDir(v->direction) == ReverseDiagDir(GetRoadStopDir(tile))) == (v->u.road.overtaking == 0)) ? 0 : 1;
02656
02657 if (!rs->IsFreeBay(side)) return VETSB_CANNOT_ENTER;
02658
02659
02660 if (GetRoadStopType(tile) == (IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? ROADSTOP_BUS : ROADSTOP_TRUCK) &&
02661 v->current_order.GetDestination() == GetStationIndex(tile)) {
02662 SetBit(v->u.road.state, RVS_IS_STOPPING);
02663 rs->AllocateDriveThroughBay(side);
02664 }
02665
02666
02667 if (side == 1) SetBit(v->u.road.state, RVS_USING_SECOND_BAY);
02668
02669 SetBit(v->u.road.state, RVS_IN_DT_ROAD_STOP);
02670 return VETSB_CONTINUE;
02671 }
02672
02673
02674
02675 if (rs->IsEntranceBusy() || !rs->HasFreeBay() || RoadVehHasArticPart(v)) return VETSB_CANNOT_ENTER;
02676
02677 SetBit(v->u.road.state, RVS_IN_ROAD_STOP);
02678
02679
02680 uint bay_nr = rs->AllocateBay();
02681 SB(v->u.road.state, RVS_USING_SECOND_BAY, 1, bay_nr);
02682
02683
02684 rs->SetEntranceBusy(true);
02685 }
02686 }
02687 }
02688
02689 return VETSB_CONTINUE;
02690 }
02691
02692
02693 static void StationHandleBigTick(Station *st)
02694 {
02695 UpdateStationAcceptance(st, true);
02696
02697 if (st->facilities == 0 && ++st->delete_ctr >= 8) delete st;
02698
02699 }
02700
02701 static inline void byte_inc_sat(byte *p)
02702 {
02703 byte b = *p + 1;
02704 if (b != 0) *p = b;
02705 }
02706
02707 static void UpdateStationRating(Station *st)
02708 {
02709 bool waiting_changed = false;
02710
02711 byte_inc_sat(&st->time_since_load);
02712 byte_inc_sat(&st->time_since_unload);
02713
02714 GoodsEntry *ge = st->goods;
02715 do {
02716
02717
02718
02719 if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP) && ge->rating < INITIAL_STATION_RATING) {
02720 ge->rating++;
02721 }
02722
02723
02724 if (HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) {
02725 byte_inc_sat(&ge->days_since_pickup);
02726
02727 int rating = 0;
02728
02729 {
02730 int b = ge->last_speed - 85;
02731 if (b >= 0)
02732 rating += b >> 2;
02733 }
02734
02735 {
02736 byte age = ge->last_age;
02737 (age >= 3) ||
02738 (rating += 10, age >= 2) ||
02739 (rating += 10, age >= 1) ||
02740 (rating += 13, true);
02741 }
02742
02743 if (IsValidCompanyID(st->owner) && HasBit(st->town->statues, st->owner)) rating += 26;
02744
02745 {
02746 byte days = ge->days_since_pickup;
02747 if (st->last_vehicle_type == VEH_SHIP) days >>= 2;
02748 (days > 21) ||
02749 (rating += 25, days > 12) ||
02750 (rating += 25, days > 6) ||
02751 (rating += 45, days > 3) ||
02752 (rating += 35, true);
02753 }
02754
02755 uint waiting = ge->cargo.Count();
02756 (rating -= 90, waiting > 1500) ||
02757 (rating += 55, waiting > 1000) ||
02758 (rating += 35, waiting > 600) ||
02759 (rating += 10, waiting > 300) ||
02760 (rating += 20, waiting > 100) ||
02761 (rating += 10, true);
02762
02763 {
02764 int or_ = ge->rating;
02765
02766
02767 ge->rating = rating = or_ + Clamp(Clamp(rating, 0, 255) - or_, -2, 2);
02768
02769
02770
02771 if (rating <= 64 && waiting >= 200) {
02772 int dec = Random() & 0x1F;
02773 if (waiting < 400) dec &= 7;
02774 waiting -= dec + 1;
02775 waiting_changed = true;
02776 }
02777
02778
02779 if (rating <= 127 && waiting != 0) {
02780 uint32 r = Random();
02781 if (rating <= (int)GB(r, 0, 7)) {
02782
02783 waiting = max((int)waiting - (int)GB(r, 8, 2) - 1, 0);
02784 waiting_changed = true;
02785 }
02786 }
02787
02788
02789
02790
02791 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
02792 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
02793 static const uint MAX_WAITING_CARGO = 1 << 15;
02794
02795 if (waiting > WAITING_CARGO_THRESHOLD) {
02796 uint difference = waiting - WAITING_CARGO_THRESHOLD;
02797 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
02798
02799 waiting = min(waiting, MAX_WAITING_CARGO);
02800 waiting_changed = true;
02801 }
02802
02803 if (waiting_changed) ge->cargo.Truncate(waiting);
02804 }
02805 }
02806 } while (++ge != endof(st->goods));
02807
02808 StationID index = st->index;
02809 if (waiting_changed) {
02810 InvalidateWindow(WC_STATION_VIEW, index);
02811 } else {
02812 InvalidateWindowWidget(WC_STATION_VIEW, index, SVW_RATINGLIST);
02813 }
02814 }
02815
02816
02817 static void StationHandleSmallTick(Station *st)
02818 {
02819 if (st->facilities == 0) return;
02820
02821 byte b = st->delete_ctr + 1;
02822 if (b >= 185) b = 0;
02823 st->delete_ctr = b;
02824
02825 if (b == 0) UpdateStationRating(st);
02826 }
02827
02828 void OnTick_Station()
02829 {
02830 if (_game_mode == GM_EDITOR) return;
02831
02832 uint i = _station_tick_ctr;
02833 if (++_station_tick_ctr > GetMaxStationIndex()) _station_tick_ctr = 0;
02834
02835 if (IsValidStationID(i)) StationHandleBigTick(GetStation(i));
02836
02837 Station *st;
02838 FOR_ALL_STATIONS(st) {
02839 StationHandleSmallTick(st);
02840
02841
02842
02843
02844 if ((_tick_counter + st->index) % 250 == 0) {
02845 StationAnimationTrigger(st, st->xy, STAT_ANIM_250_TICKS);
02846 }
02847 }
02848 }
02849
02850 void StationMonthlyLoop()
02851 {
02852
02853 }
02854
02855
02856 void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
02857 {
02858 Station *st;
02859
02860 FOR_ALL_STATIONS(st) {
02861 if (st->owner == owner &&
02862 DistanceManhattan(tile, st->xy) <= radius) {
02863 for (CargoID i = 0; i < NUM_CARGO; i++) {
02864 GoodsEntry *ge = &st->goods[i];
02865
02866 if (ge->acceptance_pickup != 0) {
02867 ge->rating = Clamp(ge->rating + amount, 0, 255);
02868 }
02869 }
02870 }
02871 }
02872 }
02873
02874 static void UpdateStationWaiting(Station *st, CargoID type, uint amount)
02875 {
02876 st->goods[type].cargo.Append(new CargoPacket(st->index, amount));
02877 SetBit(st->goods[type].acceptance_pickup, GoodsEntry::PICKUP);
02878
02879 StationAnimationTrigger(st, st->xy, STAT_ANIM_NEW_CARGO, type);
02880
02881 InvalidateWindow(WC_STATION_VIEW, st->index);
02882 st->MarkTilesDirty(true);
02883 }
02884
02885 static bool IsUniqueStationName(const char *name)
02886 {
02887 const Station *st;
02888
02889 FOR_ALL_STATIONS(st) {
02890 if (st->name != NULL && strcmp(st->name, name) == 0) return false;
02891 }
02892
02893 return true;
02894 }
02895
02902 CommandCost CmdRenameStation(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02903 {
02904 if (!IsValidStationID(p1)) return CMD_ERROR;
02905
02906 Station *st = GetStation(p1);
02907 if (!CheckOwnership(st->owner)) return CMD_ERROR;
02908
02909 bool reset = StrEmpty(text);
02910
02911 if (!reset) {
02912 if (strlen(text) >= MAX_LENGTH_STATION_NAME_BYTES) return CMD_ERROR;
02913 if (!IsUniqueStationName(text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
02914 }
02915
02916 if (flags & DC_EXEC) {
02917 free(st->name);
02918 st->name = reset ? NULL : strdup(text);
02919
02920 UpdateStationVirtCoord(st);
02921 InvalidateWindowData(WC_STATION_LIST, st->owner, 1);
02922 MarkWholeScreenDirty();
02923 }
02924
02925 return CommandCost();
02926 }
02927
02937 void FindStationsAroundTiles(TileIndex tile, int w_prod, int h_prod, StationList *stations)
02938 {
02939
02940 int max_rad = (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED);
02941
02942 for (int dy = -max_rad; dy < h_prod + max_rad; dy++) {
02943 for (int dx = -max_rad; dx < w_prod + max_rad; dx++) {
02944 TileIndex cur_tile = TileAddWrap(tile, dx, dy);
02945 if (cur_tile == INVALID_TILE || !IsTileType(cur_tile, MP_STATION)) continue;
02946
02947 Station *st = GetStationByTile(cur_tile);
02948
02949 if (st->IsBuoy()) continue;
02950
02951 if (_settings_game.station.modified_catchment) {
02952 int rad = st->GetCatchmentRadius();
02953 if (dx < -rad || dx >= rad + w_prod || dy < -rad || dy >= rad + h_prod) continue;
02954 }
02955
02956
02957
02958
02959 stations->Include(st);
02960 }
02961 }
02962 }
02963
02964 uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount)
02965 {
02966
02967 if (amount == 0) return 0;
02968
02969 Station *st1 = NULL;
02970 Station *st2 = NULL;
02971 uint best_rating1 = 0;
02972 uint best_rating2 = 0;
02973
02974 StationList all_stations;
02975 FindStationsAroundTiles(tile, w, h, &all_stations);
02976 for (Station **st_iter = all_stations.Begin(); st_iter != all_stations.End(); ++st_iter) {
02977 Station *st = *st_iter;
02978
02979
02980 if (st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) continue;
02981
02982 if (st->goods[type].rating == 0) continue;
02983
02984 if (_settings_game.order.selectgoods && st->goods[type].last_speed == 0) continue;
02985
02986 if (IsCargoInClass(type, CC_PASSENGERS)) {
02987 if (st->facilities == FACIL_TRUCK_STOP) continue;
02988 } else {
02989 if (st->facilities == FACIL_BUS_STOP) continue;
02990 }
02991
02992
02993 if (st1 == NULL || st->goods[type].rating >= best_rating1) {
02994 st2 = st1; best_rating2 = best_rating1; st1 = st; best_rating1 = st->goods[type].rating;
02995 } else if (st2 == NULL || st->goods[type].rating >= best_rating2) {
02996 st2 = st; best_rating2 = st->goods[type].rating;
02997 }
02998 }
02999
03000
03001 if (st1 == NULL) return 0;
03002
03003 if (st2 == NULL) {
03004
03005 uint moved = amount * best_rating1 / 256 + 1;
03006 UpdateStationWaiting(st1, type, moved);
03007 return moved;
03008 }
03009
03010
03011 assert(st1 != NULL);
03012 assert(st2 != NULL);
03013 assert(best_rating1 != 0 || best_rating2 != 0);
03014
03015
03016 best_rating2 >>= 1;
03017
03018
03019 uint t = (best_rating1 * (amount + 1)) / (best_rating1 + best_rating2);
03020
03021 uint moved = 0;
03022 if (t != 0) {
03023 moved = t * best_rating1 / 256 + 1;
03024 amount -= t;
03025 UpdateStationWaiting(st1, type, moved);
03026 }
03027
03028 if (amount != 0) {
03029 amount = amount * best_rating2 / 256 + 1;
03030 moved += amount;
03031 UpdateStationWaiting(st2, type, amount);
03032 }
03033
03034 return moved;
03035 }
03036
03037 void BuildOilRig(TileIndex tile)
03038 {
03039 if (!Station::CanAllocateItem()) {
03040 DEBUG(misc, 0, "Can't allocate station for oilrig at 0x%X, reverting to oilrig only", tile);
03041 return;
03042 }
03043
03044 Station *st = new Station(tile);
03045 st->town = ClosestTownFromTile(tile, UINT_MAX);
03046 st->sign.width_1 = 0;
03047
03048 st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
03049
03050 assert(IsTileType(tile, MP_INDUSTRY));
03051 MakeOilrig(tile, st->index, GetWaterClass(tile));
03052
03053 st->owner = OWNER_NONE;
03054 st->airport_flags = 0;
03055 st->airport_type = AT_OILRIG;
03056 st->xy = tile;
03057 st->bus_stops = NULL;
03058 st->truck_stops = NULL;
03059 st->airport_tile = tile;
03060 st->dock_tile = tile;
03061 st->train_tile = INVALID_TILE;
03062 st->had_vehicle_of_type = 0;
03063 st->time_since_load = 255;
03064 st->time_since_unload = 255;
03065 st->delete_ctr = 0;
03066 st->last_vehicle_type = VEH_INVALID;
03067 st->facilities = FACIL_AIRPORT | FACIL_DOCK;
03068 st->build_date = _date;
03069
03070 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
03071
03072 for (CargoID j = 0; j < NUM_CARGO; j++) {
03073 st->goods[j].acceptance_pickup = 0;
03074 st->goods[j].days_since_pickup = 255;
03075 st->goods[j].rating = INITIAL_STATION_RATING;
03076 st->goods[j].last_speed = 0;
03077 st->goods[j].last_age = 255;
03078 }
03079
03080 UpdateStationVirtCoordDirty(st);
03081 UpdateStationAcceptance(st, false);
03082 }
03083
03084 void DeleteOilRig(TileIndex tile)
03085 {
03086 Station *st = GetStationByTile(tile);
03087
03088 MakeWaterKeepingClass(tile, OWNER_NONE);
03089 MarkTileDirtyByTile(tile);
03090
03091 st->dock_tile = INVALID_TILE;
03092 st->airport_tile = INVALID_TILE;
03093 st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
03094 st->airport_flags = 0;
03095
03096 st->rect.AfterRemoveTile(st, tile);
03097
03098 UpdateStationVirtCoordDirty(st);
03099 if (st->facilities == 0) delete st;
03100 }
03101
03102 static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
03103 {
03104 if (IsDriveThroughStopTile(tile)) {
03105 for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
03106
03107 if (GetRoadOwner(tile, rt) == old_owner) {
03108 SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
03109 }
03110 }
03111 }
03112
03113 if (!IsTileOwner(tile, old_owner)) return;
03114
03115 if (new_owner != INVALID_OWNER) {
03116
03117 SetTileOwner(tile, new_owner);
03118 InvalidateWindowClassesData(WC_STATION_LIST, 0);
03119 } else {
03120 if (IsDriveThroughStopTile(tile)) {
03121
03122 DoCommand(tile, 0, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, DC_EXEC | DC_BANKRUPT, CMD_REMOVE_ROAD_STOP);
03123 assert(IsTileType(tile, MP_ROAD));
03124
03125 ChangeTileOwner(tile, old_owner, new_owner);
03126 } else {
03127 DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
03128
03129
03130
03131 if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
03132 }
03133 }
03134 }
03135
03144 static bool CanRemoveRoadWithStop(TileIndex tile, DoCommandFlag flags)
03145 {
03146 Owner road_owner = _current_company;
03147 Owner tram_owner = _current_company;
03148
03149 RoadTypes rts = GetRoadTypes(tile);
03150 if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
03151 if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
03152
03153 if ((road_owner != OWNER_TOWN && !CheckOwnership(road_owner)) || !CheckOwnership(tram_owner)) return false;
03154
03155 return road_owner != OWNER_TOWN || CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_ROAD), OWNER_TOWN, ROADTYPE_ROAD, flags);
03156 }
03157
03158 static CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
03159 {
03160 if (flags & DC_AUTO) {
03161 switch (GetStationType(tile)) {
03162 case STATION_RAIL: return_cmd_error(STR_300B_MUST_DEMOLISH_RAILROAD);
03163 case STATION_AIRPORT: return_cmd_error(STR_300E_MUST_DEMOLISH_AIRPORT_FIRST);
03164 case STATION_TRUCK: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_MUST_DEMOLISH_CARGO_TRAM_STATION : STR_3047_MUST_DEMOLISH_TRUCK_STATION);
03165 case STATION_BUS: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_MUST_DEMOLISH_PASSENGER_TRAM_STATION : STR_3046_MUST_DEMOLISH_BUS_STATION);
03166 case STATION_BUOY: return_cmd_error(STR_306A_BUOY_IN_THE_WAY);
03167 case STATION_DOCK: return_cmd_error(STR_304D_MUST_DEMOLISH_DOCK_FIRST);
03168 case STATION_OILRIG:
03169 SetDParam(0, STR_4807_OIL_RIG);
03170 return_cmd_error(STR_4800_IN_THE_WAY);
03171 }
03172 }
03173
03174 Station *st = GetStationByTile(tile);
03175
03176 switch (GetStationType(tile)) {
03177 case STATION_RAIL: return RemoveRailroadStation(st, tile, flags);
03178 case STATION_AIRPORT: return RemoveAirport(st, flags);
03179 case STATION_TRUCK:
03180 if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags))
03181 return_cmd_error(STR_3047_MUST_DEMOLISH_TRUCK_STATION);
03182 return RemoveRoadStop(st, flags, tile);
03183 case STATION_BUS:
03184 if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags))
03185 return_cmd_error(STR_3046_MUST_DEMOLISH_BUS_STATION);
03186 return RemoveRoadStop(st, flags, tile);
03187 case STATION_BUOY: return RemoveBuoy(st, flags);
03188 case STATION_DOCK: return RemoveDock(st, flags);
03189 default: break;
03190 }
03191
03192 return CMD_ERROR;
03193 }
03194
03195 void InitializeStations()
03196 {
03197
03198 _Station_pool.CleanPool();
03199 _Station_pool.AddBlockToPool();
03200
03201
03202 _RoadStop_pool.CleanPool();
03203 _RoadStop_pool.AddBlockToPool();
03204
03205 _station_tick_ctr = 0;
03206 }
03207
03208 static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
03209 {
03210 if (_settings_game.construction.build_on_slopes && AutoslopeEnabled()) {
03211
03212
03213
03214 if (!IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new))) {
03215 switch (GetStationType(tile)) {
03216 case STATION_RAIL: {
03217 DiagDirection direction = AxisToDiagDir(GetRailStationAxis(tile));
03218 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
03219 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
03220 return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform);
03221 }
03222
03223 case STATION_AIRPORT:
03224 return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform);
03225
03226 case STATION_TRUCK:
03227 case STATION_BUS: {
03228 DiagDirection direction = GetRoadStopDir(tile);
03229 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
03230 if (IsDriveThroughStopTile(tile)) {
03231 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
03232 }
03233 return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform);
03234 }
03235
03236 default: break;
03237 }
03238 }
03239 }
03240 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
03241 }
03242
03243
03244 extern const TileTypeProcs _tile_type_station_procs = {
03245 DrawTile_Station,
03246 GetSlopeZ_Station,
03247 ClearTile_Station,
03248 GetAcceptedCargo_Station,
03249 GetTileDesc_Station,
03250 GetTileTrackStatus_Station,
03251 ClickTile_Station,
03252 AnimateTile_Station,
03253 TileLoop_Station,
03254 ChangeTileOwner_Station,
03255 NULL,
03256 VehicleEnter_Station,
03257 GetFoundation_Station,
03258 TerraformTile_Station,
03259 };