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