# station_cmd.cpp

Go to the documentation of this file.

    00001 /* $Id: station_cmd.cpp 14311 2008-09-13 12:57:17Z rubidium $ */
    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" /* For drawing catenary/checking road removal */
    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   /* check around to see if there's any stations there */
    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   /* No industry */
    00171   if (!IsTileType(tile, MP_INDUSTRY)) return false;
    00172
    00173   const Industry *ind = GetIndustryByTile(tile);
    00174
    00175   /* No extractive industry */
    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     /* The industry extracts something non-liquid, i.e. no oil or plastic, so it is a mine */
    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   /* No industry */
    00214   if (!IsTileType(tile, MP_INDUSTRY)) return false;
    00215
    00216   const Industry *ind = GetIndustryByTile(tile);
    00217
    00218   /* No extractive industry */
    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     /* The industry produces wood. */
    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,                                      /* 0 */
    00245     1 << M(STR_SV_STNAME_AIRPORT),          /* 1 */
    00246     1 << M(STR_SV_STNAME_OILFIELD),         /* 2 */
    00247     1 << M(STR_SV_STNAME_DOCKS),            /* 3 */
    00248     0x1FF << M(STR_SV_STNAME_BUOY_1),       /* 4 */
    00249     1 << M(STR_SV_STNAME_HELIPORT),         /* 5 */
    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   /* check default names */
    00273   tmp = free_names & _gen_station_name_bits[flag];
    00274   if (tmp != 0) {
    00275     found = FindFirstBit(tmp);
    00276     goto done;
    00277   }
    00278
    00279   /* check mine? */
    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   /* check close enough to town to get central as name? */
    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   /* Check lakeside */
    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   /* Check woods */
    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   /* check elevation compared to town */
    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   /* check direction compared to town */
    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   /* expand the region by rad tiles on each side
    00453    * while making sure that we remain inside the board. */
    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   /* expand the region by rad tiles on each side
    00501    * while making sure that we remain inside the board. */
    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   /* Don't update acceptance for a buoy */
    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   /* old accepted goods types */
    00560   uint old_acc = GetAcceptanceMask(st);
    00561
    00562   /* Put all the tiles that span an area in the table. */
    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   /* And retrieve the acceptance. */
    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   /* Adjust in case our station only accepts fewer kinds of goods */
    00604   for (CargoID i = 0; i < NUM_CARGO; i++) {
    00605     uint amt = min(accepts[i], 15);
    00606
    00607     /* Make sure the station can accept the goods type. */
    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   /* Only show a message in case the acceptance was actually changed. */
    00617   uint new_acc = GetAcceptanceMask(st);
    00618   if (old_acc == new_acc)
    00619     return;
    00620
    00621   /* show a message to report that the acceptance was changed? */
    00622   if (show_msg && st->owner == _local_player && st->facilities) {
    00623     /* List of accept and reject strings for different number of
    00624      * cargo types */
    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     /* Array of accepted and rejected cargo types */
    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     /* Test each cargo type to see if its acceptange has changed */
    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           /* New cargo is accepted */
    00645           accepts[num_acc++] = i;
    00646         }
    00647       } else {
    00648         if (HasBit(old_acc, i) && num_rej < lengthof(rejects)) {
    00649           /* Old cargo is no longer accepted */
    00650           rejects[num_rej++] = i;
    00651         }
    00652       }
    00653     }
    00654
    00655     /* Show news message if there are any changes */
    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   /* redraw the station view since acceptance changed */
    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; /* no tiles belong to this station */
    00669
    00670   /* clamp sign coord to be inside the station rect */
    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   /* station remains but it probably lost some parts - station sign should stay in the station boundaries */
    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     /* Prohibit building if
    00719      *   1) The tile is "steep" (i.e. stretches two height levels)
    00720      * -OR-
    00721      *   2) The tile is non-flat if
    00722      *     a) the player building is an "old-school" AI
    00723      *   -OR-
    00724      *     b) the build_on_slopes switch is disabled
    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       /* need to check so the entrance to the station is not pointing at a slope.
    00734        * This must be valid for all station tiles, as the user can remove single station tiles. */
    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     /* get corresponding flat level and make sure that all parts of the station have the same level. */
    00746     if (allowed_z == -1) {
    00747       /* first tile */
    00748       allowed_z = flat_z;
    00749     } else if (allowed_z != flat_z) {
    00750       return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
    00751     }
    00752
    00753     /* if station is set, then we have special handling to allow building on top of already existing stations.
    00754      * so station points to INVALID_STATION if we can build on any station.
    00755      * Or it points to a station if we're only allowed to build on exactly that station. */
    00756     if (station != NULL && IsTileType(tile_cur, MP_STATION)) {
    00757       if (!IsRailwayStation(tile_cur)) {
    00758         return ClearTile_Station(tile_cur, DC_AUTO); // get error message
    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     /* determine new size of train station region.. */
    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     /* do not allow modifying non-uniform stations,
    00794      * the uniform-stations code wouldn't handle it well */
    00795     BEGIN_TILE_LOOP(t, st->trainst_w, st->trainst_h, st->train_tile)
    00796       if (!st->TileBelongsToRailStation(t)) { // there may be adjoined station
    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     /* check so the orientation is the same */
    00803     if (GetRailStationAxis(st->train_tile) != axis) {
    00804       _error_message = STR_NONUNIFORM_STATIONS_DISALLOWED;
    00805       return false;
    00806     }
    00807
    00808     /* check if the new station adjoins the old station in either direction */
    00809     if (curw == w && st->train_tile == tile + TileDiffXY(0, h)) {
    00810       /* above */
    00811       curh += h;
    00812     } else if (curw == w && st->train_tile == tile - TileDiffXY(0, curh)) {
    00813       /* below */
    00814       tile -= TileDiffXY(0, curh);
    00815       curh += h;
    00816     } else if (curh == h && st->train_tile == tile + TileDiffXY(w, 0)) {
    00817       /* to the left */
    00818       curw += w;
    00819     } else if (curh == h && st->train_tile == tile - TileDiffXY(curw, 0)) {
    00820       /* to the right */
    00821       tile -= TileDiffXY(curw, 0);
    00822       curw += w;
    00823     } else {
    00824       _error_message = STR_NONUNIFORM_STATIONS_DISALLOWED;
    00825       return false;
    00826     }
    00827   }
    00828   /* make sure the final size is not too big. */
    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   /* now tile contains the new value for st->train_tile
    00835    * curw, curh contain the new value for width and height */
    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     /* Custom layout defined, follow it. */
    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   /* Does the authority allow this? */
    00904   if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile_org)) return CMD_ERROR;
    00905   if (!ValParamRailtype((RailType)(p2 & 0xF))) return CMD_ERROR;
    00906
    00907   /* unpack parameters */
    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   /* these values are those that will be stored in train_tile and station_platforms */
    00922   uint finalvalues[3];
    00923   finalvalues[0] = tile_org;
    00924   finalvalues[1] = w_org;
    00925   finalvalues[2] = h_org;
    00926
    00927   /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
    00928   StationID est = INVALID_STATION;
    00929   /* If DC_EXEC is in flag, do not want to pass it to CheckFlatLandBelow, because of a nice bug
    00930    * for detail info, see:
    00931    * https://sourceforge.net/tracker/index.php?func=detail&aid=1029064&group_id=103924&atid=636365 */
    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         /* You can't build an adjacent station over the top of one that
    00943          * already exists. */
    00944         return_cmd_error(STR_MUST_REMOVE_RAILWAY_STATION_FIRST);
    00945       } else {
    00946         /* Extend the current station, and don't check whether it will
    00947          * be near any other stations. */
    00948         st = GetStation(est);
    00949         check_surrounding = false;
    00950       }
    00951     } else {
    00952       /* There's no station here. Don't check the tiles surrounding this
    00953        * one if the player wanted to build an adjacent station. */
    00954       if (HasBit(p1, 24)) check_surrounding = false;
    00955     }
    00956   }
    00957
    00958   if (check_surrounding) {
    00959     /* Make sure there are no similar stations around us. */
    00960     st = GetStationAround(tile_org, w_org, h_org, est);
    00961     if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
    00962   }
    00963
    00964   /* See if there is a deleted station close to us. */
    00965   if (st == NULL) st = GetClosestStationFromTile(tile_org);
    00966
    00967   if (st != NULL) {
    00968     /* Reuse an existing station. */
    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       /* check if we want to expanding an already existing station? */
    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     /* XXX can't we pack this in the "else" part of the if above? */
    00981     if (!st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TEST)) return CMD_ERROR;
    00982   } else {
    00983     /* allocate and initialize new station */
    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   /* Check if the given station class is valid */
    00999   if (GB(p2, 8, 8) >= GetNumStationClasses()) return CMD_ERROR;
    01000
    01001   /* Check if we can allocate a custom stationspec to this station */
    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     /* Perform NewStation checks */
    01008
    01009     /* Check if the station size is permitted */
    01010     if (HasBit(statspec->disallowed_platforms, numtracks - 1) || HasBit(statspec->disallowed_lengths, plat_len - 1)) {
    01011       return CMD_ERROR;
    01012     }
    01013
    01014     /* Check if the station is buildable */
    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     /* Now really clear the land below the station
    01027      * It should never return CMD_ERROR.. but you never know ;)
    01028      * (a bit strange function name for it, but it really does clear the land, when DC_EXEC is in flags) */
    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           /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
    01059           uint32 platinfo = GetPlatformInfo(AXIS_X, 0, plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
    01060
    01061           /* As the station is not yet completely finished, the station does not yet exist. */
    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); // perpendicular to tile_delta
    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   /* too small? */
    01093   if (w != 0 && h != 0) {
    01094     /* check the left side, x = constant, y changes */
    01095     for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(0, i));) {
    01096       /* the left side is unused? */
    01097       if (++i == h) {
    01098         tile += TileDiffXY(1, 0);
    01099         w--;
    01100         goto restart;
    01101       }
    01102     }
    01103
    01104     /* check the right side, x = constant, y changes */
    01105     for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(w - 1, i));) {
    01106       /* the right side is unused? */
    01107       if (++i == h) {
    01108         w--;
    01109         goto restart;
    01110       }
    01111     }
    01112
    01113     /* check the upper side, y = constant, x changes */
    01114     for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(i, 0));) {
    01115       /* the left side is unused? */
    01116       if (++i == w) {
    01117         tile += TileDiffXY(0, 1);
    01118         h--;
    01119         goto restart;
    01120       }
    01121     }
    01122
    01123     /* check the lower side, y = constant, x changes */
    01124     for (uint i = 0; !st->TileBelongsToRailStation(tile + TileDiffXY(i, h - 1));) {
    01125       /* the left side is unused? */
    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   /* Count of the number of tiles removed */
    01152   int quantity = 0;
    01153
    01154   if (tile >= MapSize() || start >= MapSize()) return CMD_ERROR;
    01155
    01156   /* make sure sx,sy are smaller than ex,ey */
    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   /* Do the action for every tile into the area */
    01169   BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) {
    01170     /* Make sure the specified tile is a railroad station */
    01171     if (!IsTileType(tile2, MP_STATION) || !IsRailwayStation(tile2)) {
    01172       continue;
    01173     }
    01174
    01175     /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
    01176     if (!EnsureNoVehicleOnGround(tile2)) {
    01177       continue;
    01178     }
    01179
    01180     /* Check ownership of station */
    01181     Station *st = GetStationByTile(tile2);
    01182     if (_current_player != OWNER_WATER && !CheckOwnership(st->owner)) {
    01183       continue;
    01184     }
    01185
    01186     /* Do not allow removing from stations if non-uniform stations are not enabled
    01187      * The check must be here to give correct error message
    01188      */
    01189     if (!_patches.nonuniform_stations) return_cmd_error(STR_NONUNIFORM_STATIONS_DISALLOWED);
    01190
    01191     /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
    01192     quantity++;
    01193
    01194     if (flags & DC_EXEC) {
    01195       /* read variables before the station tile is removed */
    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       /* now we need to make the "spanned" area of the railway station smaller
    01208        * if we deleted something at the edges.
    01209        * we also need to adjust train_tile. */
    01210       MakeRailwayStationAreaSmaller(st);
    01211       st->MarkTilesDirty(false);
    01212       UpdateStationSignCoord(st);
    01213
    01214       /* if we deleted the whole station, delete the train facility. */
    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   /* If we've not removed any tiles, give an error */
    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   /* if there is flooding and non-uniform stations are enabled, remove platforms tile by tile */
    01234   if (_current_player == OWNER_WATER && _patches.nonuniform_stations)
    01235     return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAILROAD_STATION);
    01236
    01237   /* Current player owns the station? */
    01238   if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
    01239     return CMD_ERROR;
    01240
    01241   /* determine width and height of platforms */
    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   /* clear all areas of the station */
    01250   do {
    01251     int w_bak = w;
    01252     do {
    01253       /* for nonuniform stations, only remove tiles that are actually train station tiles */
    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           /* read variables before the station tile is removed */
    01260           Track track = GetRailStationTrack(tile);
    01261           Owner owner = GetTileOwner(tile); // _current_player can be OWNER_WATER
    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     /* we have no roadstop of the type yet, so write a "primary stop" */
    01303     return primary_stop;
    01304   } else {
    01305     /* there are stops already, so append to the end of the list */
    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   /* Trams only have drive through stops */
    01332   if (!is_drive_through && HasBit(rts, ROADTYPE_TRAM)) return CMD_ERROR;
    01333
    01334   /* Saveguard the parameters */
    01335   if (!IsValidDiagDirection((DiagDirection)p1)) return CMD_ERROR;
    01336   /* If it is a drive-through stop check for valid axis */
    01337   if (is_drive_through && !IsValidAxis((Axis)p1)) return CMD_ERROR;
    01338   /* Road bits in the wrong direction */
    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   /* Not allowed to build over this road */
    01348   if (build_over_road) {
    01349     /* there is a road, check if we can build road+tram stop over it */
    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     /* there is a tram, check if we can build road+tram stop over it */
    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     /* Don't allow building the roadstop when vehicles are already driving on it */
    01369     if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
    01370
    01371     /* Do not remove roadtypes! */
    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   /* Find a station close to us */
    01387   if (st == NULL) st = GetClosestStationFromTile(tile);
    01388
    01389   /* give us a road stop in the list, and check if something went wrong */
    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     /* allocate and initialize new station */
    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     /* Insert into linked list of RoadStops */
    01425     RoadStop **currstop = FindRoadStopSpot(type, st);
    01426     *currstop = road_stop;
    01427
    01428     /*initialize an empty station */
    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) { // truck stop
    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   /* don't do the check for drive-through road stops when company bankrupts */
    01485   if (IsDriveThroughStopTile(tile) && (flags & DC_BANKRUPT)) {
    01486     /* remove the 'going through road stop' status from all vehicles on that tile */
    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       /* removed the first stop in the list */
    01495       *primary_stop = cur_stop->next;
    01496       /* removed the only stop? */
    01497       if (*primary_stop == NULL) {
    01498         st->facilities &= (is_truck ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP);
    01499       }
    01500     } else {
    01501       /* tell the predecessor in the list to skip this stop */
    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   /* Make sure the specified tile is a road stop of the correct type */
    01528   if (!IsTileType(tile, MP_STATION) || !IsRoadStop(tile) || (uint32)GetRoadStopType(tile) != p2) return CMD_ERROR;
    01529   Station *st = GetStationByTile(tile);
    01530   /* Save the stop info before it is removed */
    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   /* If the stop was a drive-through stop replace the road */
    01541   if ((flags & DC_EXEC) && CmdSucceeded(ret) && is_drive_through) {
    01542     /* Rebuild the drive throuhg road stop. As a road stop can only be
    01543      * removed by the owner of the roadstop, _current_player is the
    01544      * owner of the road stop. */
    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 /* FIXME -- need to move to its corresponding Airport variable*/
    01553
    01554 /* Country Airfield (small) */
    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 /* City Airport (large) */
    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 /* Metropolitain Airport (large) - 2 runways */
    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 /* International Airport (large) - 2 runways */
    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 /* Intercontinental Airport (vlarge) - 4 runways */
    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 /* Commuter Airfield (small) */
    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 /* Heliport */
    01617 static const byte _airport_sections_heliport[] = {
    01618   66,
    01619 };
    01620
    01621 /* Helidepot */
    01622 static const byte _airport_sections_helidepot[] = {
    01623   124, 32,
    01624   122, 123
    01625 };
    01626
    01627 /* Helistation */
    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,           // Country Airfield (small)
    01635   _airport_sections_town,              // City Airport (large)
    01636   _airport_sections_heliport,          // Heliport
    01637   _airport_sections_metropolitan,      // Metropolitain Airport (large)
    01638   _airport_sections_international,     // International Airport (xlarge)
    01639   _airport_sections_commuter,          // Commuter Airport (small)
    01640   _airport_sections_helidepot,         // Helidepot
    01641   _airport_sections_intercontinental,  // Intercontinental Airport (xxlarge)
    01642   _airport_sections_helistation        // Helistation
    01643 };
    01644
    01651 CommandCost CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
    01652 {
    01653   bool airport_upgrade = true;
    01654
    01655   /* Check if a valid, buildable airport was chosen for construction */
    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   /* Check if local auth refuses a new airport */
    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   /* Find a station close to us */
    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     /* allocate and initialize new station */
    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     /* if airport was demolished while planes were en-route to it, the
    01737      * positions can no longer be the same (v->u.air.pos), since different
    01738      * airports have different indexes. So update all planes en-route to this
    01739      * airport. Only update if
    01740      * 1. airport is upgraded
    01741      * 2. airport is added to existing station (unfortunately unavoideable)
    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   /* allocate and initialize new station */
    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     /* Buoys are marked in the Station struct by this flag. Yes, it is this
    01842      * braindead.. */
    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   /* XXX: strange stuff */
    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   /* remove the buoy if there is a ship on tile when company goes bankrupt... */
    01891   if (!(flags & DC_BANKRUPT) && !EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
    01892
    01893   if (flags & DC_EXEC) {
    01894     st->dock_tile = 0;
    01895     /* Buoys are marked in the Station struct by this flag. Yes, it is this
    01896      * braindead.. */
    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     /* We have to set the water tile's state to the same state as before the
    01903      * buoy was placed. Otherwise one could plant a buoy on a canal edge,
    01904      * remove it and flood the land (if the canal edge is at level 0) */
    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   /* Docks cannot be placed on rapids */
    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   /* Get the water class of the water tile before it is cleared.*/
    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   /* middle */
    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   /* Find a station close to us */
    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     /* allocate and initialize new station */
    01989     /* allocate and initialize new station */
    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     /* Some stations are not owner by a player, namely oil rigs */
    02085     palette = PALETTE_TO_GREY;
    02086   }
    02087
    02088   /* don't show foundation for docks */
    02089   if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile))
    02090     DrawFoundation(ti, FOUNDATION_LEVELED);
    02091
    02092   if (IsCustomStationSpecIndex(ti->tile)) {
    02093     /* look for customization */
    02094     st = GetStationByTile(ti->tile);
    02095     statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
    02096
    02097     //debug("Cust-o-mized %p", statspec);
    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       /* Ensure the chosen tile layout is valid for this custom station */
    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   /* not used */
    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       /* buoy is coded as a station, it is always on open water */
    02260       if (IsBuoy(tile)) {
    02261         trackbits = TRACK_BIT_ALL;
    02262         /* remove tracks that connect NE map edge */
    02263         if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
    02264         /* remove tracks that connect NW map edge */
    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   /* FIXME -- GetTileTrackStatus_Station -> animated stationtiles
    02293    * hardcoded.....not good */
    02294   switch (GetStationType(tile)) {
    02295     case STATION_AIRPORT:
    02296       switch (GetStationGfx(tile)) {
    02297         case GFX_RADAR_LARGE_FIRST:
    02298         case GFX_WINDSACK_FIRST : // for small airport
    02299         case GFX_RADAR_INTERNATIONAL_FIRST:
    02300         case GFX_RADAR_METROPOLITAN_FIRST:
    02301         case GFX_RADAR_DISTRICTWE_FIRST: // radar district W-E airport
    02302         case GFX_WINDSACK_INTERCON_FIRST : // for intercontinental airport
    02303           AddAnimatedTile(tile);
    02304           break;
    02305       }
    02306       break;
    02307
    02308     case STATION_DOCK:
    02309       if (GetTileSlope(tile, NULL) != SLOPE_FLAT) break; // only handle water part
    02310     /* FALL THROUGH */
    02311     case STATION_OILRIG: //(station part)
    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; // first sprite
    02325     StationGfx to;   // last sprite
    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); /* enter station */
    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         /* Attempt to allocate a parking bay in a road stop */
    02401         RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile));
    02402
    02403         if (IsDriveThroughStopTile(tile)) {
    02404           /* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */
    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           /* Check if the vehicle is stopping at this road stop */
    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           /* Indicate if vehicle is using second bay. */
    02417           if (side == 1) SetBit(v->u.road.state, RVS_USING_SECOND_BAY);
    02418           /* Indicate a drive-through stop */
    02419           SetBit(v->u.road.state, RVS_IN_DT_ROAD_STOP);
    02420           return VETSB_CONTINUE;
    02421         }
    02422
    02423         /* For normal (non drive-through) road stops */
    02424         /* Check if station is busy or if there are no free bays or whether it is a articulated vehicle. */
    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         /* Allocate a bay and update the road state */
    02430         uint bay_nr = rs->AllocateBay();
    02431         SB(v->u.road.state, RVS_USING_SECOND_BAY, 1, bay_nr);
    02432
    02433         /* Mark the station entrace as busy */
    02434         rs->SetEntranceBusy(true);
    02435       }
    02436     }
    02437   }
    02438
    02439   return VETSB_CONTINUE;
    02440 }
    02441
    02442 /* this function is called for one station each tick */
    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     /* Slowly increase the rating back to his original level in the case we
    02463      *  didn't deliver cargo yet to this station. This happens when a bribe
    02464      *  failed while you didn't moved that cargo yet to a station. */
    02465     if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP) && ge->rating < INITIAL_STATION_RATING) {
    02466       ge->rating++;
    02467     }
    02468
    02469     /* Only change the rating if we are moving this cargo */
    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; // old rating
    02511
    02512         /* only modify rating in steps of -2, -1, 0, 1 or 2 */
    02513         ge->rating = rating = or_ + Clamp(Clamp(rating, 0, 255) - or_, -2, 2);
    02514
    02515         /* if rating is <= 64 and more than 200 items waiting,
    02516          * remove some random amount of goods from the station */
    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         /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
    02525         if (rating <= 127 && waiting != 0) {
    02526           uint32 r = Random();
    02527           if (rating <= (int)GB(r, 0, 7)) {
    02528             /* Need to have int, otherwise it will just overflow etc. */
    02529             waiting = max((int)waiting - (int)GB(r, 8, 2) - 1, 0);
    02530             waiting_changed = true;
    02531           }
    02532         }
    02533
    02534         /* At some point we really must cap the cargo. Previously this
    02535          * was a strict 4095, but now we'll have a less strict, but
    02536          * increasingly agressive truncation of the amount of cargo. */
    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); // update whole window
    02557   } else {
    02558     InvalidateWindowWidget(WC_STATION_VIEW, index, SVW_RATINGLIST); // update only ratings list
    02559   }
    02560 }
    02561
    02562 /* called for every station each tick */
    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; // width and height of the "producer" of the cargo
    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; // bouys don't accept cargo
    02697
    02698
    02699     if (_patches.modified_catchment) {
    02700       /* min and max coordinates of the producer relative */
    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     /* Insert the station in the set. This will fail if it has
    02728      * already been added.
    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;  // Station with best rating
    02740   Station *st2 = NULL;  // Second best station
    02741   uint best_rating1 = 0;  // rating of st1
    02742   uint best_rating2 = 0;  // rating of st2
    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     /* Is the station reserved exclusively for somebody else? */
    02749     if (st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) continue;
    02750
    02751     if (st->goods[type].rating == 0) continue; // Lowest possible rating, better not to give cargo anymore
    02752
    02753     if (_patches.selectgoods && st->goods[type].last_speed == 0) continue; // Selectively servicing stations, and not this one
    02754
    02755     if (IsCargoInClass(type, CC_PASSENGERS)) {
    02756       if (st->facilities == FACIL_TRUCK_STOP) continue; // passengers are never served by just a truck stop
    02757     } else {
    02758       if (st->facilities == FACIL_BUS_STOP) continue; // non-passengers are never served by just a bus stop
    02759     }
    02760
    02761     /* This station can be used, add it to st1/st2 */
    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   /* no stations around at all? */
    02770   if (st1 == NULL) return 0;
    02771
    02772   if (st2 == NULL) {
    02773     /* only one station around */
    02774     uint moved = amount * best_rating1 / 256 + 1;
    02775     UpdateStationWaiting(st1, type, moved);
    02776     return moved;
    02777   }
    02778
    02779   /* several stations around, the best two (highest rating) are in st1 and st2 */
    02780   assert(st1 != NULL);
    02781   assert(st2 != NULL);
    02782   assert(best_rating1 != 0 || best_rating2 != 0);
    02783
    02784   /* the 2nd highest one gets a penalty */
    02785   best_rating2 >>= 1;
    02786
    02787   /* amount given to station 1 */
    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; // do not set st->owner for buoys
    02874     RebuildStationLists();
    02875     InvalidateWindowClasses(WC_STATION_LIST);
    02876   } else {
    02877     if (IsDriveThroughStopTile(tile)) {
    02878       /* Remove the drive-through road stop */
    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       /* Change owner of tile and all roadtypes */
    02882       ChangeTileOwner(tile, old_player, new_player);
    02883     } else {
    02884       DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
    02885       /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
    02886        * Update owner of buoy if it was not removed (was in orders).
    02887        * Do not update when owned by OWNER_WATER (sea and rivers). */
    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   /* The road can always be cleared if it was not a town-owned road */
    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   /* Clean the station pool and create 1 block in it */
    02950   _Station_pool.CleanPool();
    02951   _Station_pool.AddBlockToPool();
    02952
    02953   /* Clean the roadstop pool and create 1 block in it */
    02954   _RoadStop_pool.CleanPool();
    02955   _RoadStop_pool.AddBlockToPool();
    02956
    02957   _station_tick_ctr = 0;
    02958
    02959 }
    02960
    02961
    02962 void AfterLoadStations()
    02963 {
    02964   /* Update the speclists of all stations to point to the currently loaded custom stations. */
    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     /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
    02981      *       TTDP does not call it.
    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,           /* draw_tile_proc */
    03015   GetSlopeZ_Station,          /* get_slope_z_proc */
    03016   ClearTile_Station,          /* clear_tile_proc */
    03017   GetAcceptedCargo_Station,   /* get_accepted_cargo_proc */
    03018   GetTileDesc_Station,        /* get_tile_desc_proc */
    03019   GetTileTrackStatus_Station, /* get_tile_track_status_proc */
    03020   ClickTile_Station,          /* click_tile_proc */
    03021   AnimateTile_Station,        /* animate_tile_proc */
    03022   TileLoop_Station,           /* tile_loop_clear */
    03023   ChangeTileOwner_Station,    /* change_tile_owner_clear */
    03024   NULL,                       /* get_produced_cargo_proc */
    03025   VehicleEnter_Station,       /* vehicle_enter_tile_proc */
    03026   GetFoundation_Station,      /* get_foundation_proc */
    03027   TerraformTile_Station,      /* terraform_tile_proc */
    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   /* Index was saved in some versions, but this is not needed */
    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), // bus/lorry tile
    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   /* alpha_order was stored here in savegame format 0 - 3 */
    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), // Truck/bus stop status
    03077   SLE_CONDNULL(1, 0, 4), // Blocked months
    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), /* Ex last-vehicle */
    03084   SLE_CONDVAR(Station, last_vehicle_type,          SLE_UINT8,                  26, SL_MAX_VERSION),
    03085
    03086   /* Was custom station class and id */
    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   /* Used by newstations for graphic variations */
    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   /* reserve extra space in savegame here. (currently 32 bytes) */
    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         /* Don't construct the packet with station here, because that'll fail with old savegames */
    03155         CargoPacket *cp = new CargoPacket();
    03156         /* In old versions, enroute_from used 0xFF as INVALID_STATION */
    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     /* Allocate speclist memory when loading a game */
    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   /* Write the stations */
    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     /* this means it's an oldstyle savegame without support for nonuniform stations */
    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   /* This is to ensure all pointers are within the limits of _stations_size */
    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 };