station_cmd.cpp

Go to the documentation of this file.
00001 /* $Id: station_cmd.cpp 19665 2010-04-17 22:27:49Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "aircraft.h"
00014 #include "bridge_map.h"
00015 #include "cmd_helper.h"
00016 #include "landscape.h"
00017 #include "viewport_func.h"
00018 #include "command_func.h"
00019 #include "town.h"
00020 #include "news_func.h"
00021 #include "train.h"
00022 #include "roadveh.h"
00023 #include "industry.h"
00024 #include "newgrf_cargo.h"
00025 #include "newgrf_station.h"
00026 #include "pathfinder/yapf/yapf_cache.h"
00027 #include "road_internal.h" /* For drawing catenary/checking road removal */
00028 #include "variables.h"
00029 #include "autoslope.h"
00030 #include "water.h"
00031 #include "station_gui.h"
00032 #include "strings_func.h"
00033 #include "functions.h"
00034 #include "window_func.h"
00035 #include "date_func.h"
00036 #include "vehicle_func.h"
00037 #include "string_func.h"
00038 #include "animated_tile_func.h"
00039 #include "elrail_func.h"
00040 #include "station_base.h"
00041 #include "roadstop_base.h"
00042 #include "newgrf_railtype.h"
00043 #include "waypoint_base.h"
00044 #include "waypoint_func.h"
00045 #include "pbs.h"
00046 #include "debug.h"
00047 #include "core/random_func.hpp"
00048 #include "company_base.h"
00049 #include "newgrf.h"
00050 #include "table/airporttile_ids.h"
00051 
00052 #include "table/strings.h"
00053 
00060 bool IsHangar(TileIndex t)
00061 {
00062   assert(IsTileType(t, MP_STATION));
00063 
00064   /* If the tile isn't an airport there's no chance it's a hangar. */
00065   if (!IsAirport(t)) return false;
00066 
00067   const Station *st = Station::GetByTile(t);
00068   const AirportSpec *as = st->GetAirportSpec();
00069 
00070   for (uint i = 0; i < as->nof_depots; i++) {
00071     if (st->GetHangarTile(i) == t) return true;
00072   }
00073 
00074   return false;
00075 }
00076 
00084 template <class T>
00085 bool GetStationAround(TileArea ta, StationID closest_station, T **st)
00086 {
00087   /* check around to see if there's any stations there */
00088   TILE_LOOP(tile_cur, ta.w + 2, ta.h + 2, ta.tile - TileDiffXY(1, 1)) {
00089     if (IsTileType(tile_cur, MP_STATION)) {
00090       StationID t = GetStationIndex(tile_cur);
00091 
00092       if (closest_station == INVALID_STATION) {
00093         if (T::IsValidID(t)) closest_station = t;
00094       } else if (closest_station != t) {
00095         _error_message = STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING;
00096         return false;
00097       }
00098     }
00099   }
00100   *st = (closest_station == INVALID_STATION) ? NULL : T::Get(closest_station);
00101   return true;
00102 }
00103 
00109 typedef bool (*CMSAMatcher)(TileIndex tile);
00110 
00117 static int CountMapSquareAround(TileIndex tile, CMSAMatcher cmp)
00118 {
00119   int num = 0;
00120 
00121   for (int dx = -3; dx <= 3; dx++) {
00122     for (int dy = -3; dy <= 3; dy++) {
00123       TileIndex t = TileAddWrap(tile, dx, dy);
00124       if (t != INVALID_TILE && cmp(t)) num++;
00125     }
00126   }
00127 
00128   return num;
00129 }
00130 
00136 static bool CMSAMine(TileIndex tile)
00137 {
00138   /* No industry */
00139   if (!IsTileType(tile, MP_INDUSTRY)) return false;
00140 
00141   const Industry *ind = Industry::GetByTile(tile);
00142 
00143   /* No extractive industry */
00144   if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_EXTRACTIVE) == 0) return false;
00145 
00146   for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
00147     /* The industry extracts something non-liquid, i.e. no oil or plastic, so it is a mine.
00148      * Also the production of passengers and mail is ignored. */
00149     if (ind->produced_cargo[i] != CT_INVALID &&
00150         (CargoSpec::Get(ind->produced_cargo[i])->classes & (CC_LIQUID | CC_PASSENGERS | CC_MAIL)) == 0) {
00151       return true;
00152     }
00153   }
00154 
00155   return false;
00156 }
00157 
00163 static bool CMSAWater(TileIndex tile)
00164 {
00165   return IsTileType(tile, MP_WATER) && IsWater(tile);
00166 }
00167 
00173 static bool CMSATree(TileIndex tile)
00174 {
00175   return IsTileType(tile, MP_TREES);
00176 }
00177 
00183 static bool CMSAForest(TileIndex tile)
00184 {
00185   /* No industry */
00186   if (!IsTileType(tile, MP_INDUSTRY)) return false;
00187 
00188   const Industry *ind = Industry::GetByTile(tile);
00189 
00190   /* No extractive industry */
00191   if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_ORGANIC) == 0) return false;
00192 
00193   for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
00194     /* The industry produces wood. */
00195     if (ind->produced_cargo[i] != CT_INVALID && CargoSpec::Get(ind->produced_cargo[i])->label == 'WOOD') return true;
00196   }
00197 
00198   return false;
00199 }
00200 
00201 #define M(x) ((x) - STR_SV_STNAME)
00202 
00203 enum StationNaming {
00204   STATIONNAMING_RAIL,
00205   STATIONNAMING_ROAD,
00206   STATIONNAMING_AIRPORT,
00207   STATIONNAMING_OILRIG,
00208   STATIONNAMING_DOCK,
00209   STATIONNAMING_HELIPORT,
00210 };
00211 
00213 struct StationNameInformation {
00214   uint32 free_names; 
00215   bool *indtypes;    
00216 };
00217 
00226 static bool FindNearIndustryName(TileIndex tile, void *user_data)
00227 {
00228   /* All already found industry types */
00229   StationNameInformation *sni = (StationNameInformation*)user_data;
00230   if (!IsTileType(tile, MP_INDUSTRY)) return false;
00231 
00232   /* If the station name is undefined it means that it doesn't name a station */
00233   IndustryType indtype = GetIndustryType(tile);
00234   if (GetIndustrySpec(indtype)->station_name == STR_UNDEFINED) return false;
00235 
00236   /* In all cases if an industry that provides a name is found two of
00237    * the standard names will be disabled. */
00238   sni->free_names &= ~(1 << M(STR_SV_STNAME_OILFIELD) | 1 << M(STR_SV_STNAME_MINES));
00239   return !sni->indtypes[indtype];
00240 }
00241 
00242 static StringID GenerateStationName(Station *st, TileIndex tile, StationNaming name_class)
00243 {
00244   static const uint32 _gen_station_name_bits[] = {
00245     0,                                       // STATIONNAMING_RAIL
00246     0,                                       // STATIONNAMING_ROAD
00247     1U << M(STR_SV_STNAME_AIRPORT),          // STATIONNAMING_AIRPORT
00248     1U << M(STR_SV_STNAME_OILFIELD),         // STATIONNAMING_OILRIG
00249     1U << M(STR_SV_STNAME_DOCKS),            // STATIONNAMING_DOCK
00250     1U << M(STR_SV_STNAME_HELIPORT),         // STATIONNAMING_HELIPORT
00251   };
00252 
00253   const Town *t = st->town;
00254   uint32 free_names = UINT32_MAX;
00255 
00256   bool indtypes[NUM_INDUSTRYTYPES];
00257   memset(indtypes, 0, sizeof(indtypes));
00258 
00259   const Station *s;
00260   FOR_ALL_STATIONS(s) {
00261     if (s != st && s->town == t) {
00262       if (s->indtype != IT_INVALID) {
00263         indtypes[s->indtype] = true;
00264         continue;
00265       }
00266       uint str = M(s->string_id);
00267       if (str <= 0x20) {
00268         if (str == M(STR_SV_STNAME_FOREST)) {
00269           str = M(STR_SV_STNAME_WOODS);
00270         }
00271         ClrBit(free_names, str);
00272       }
00273     }
00274   }
00275 
00276   TileIndex indtile = tile;
00277   StationNameInformation sni = { free_names, indtypes };
00278   if (CircularTileSearch(&indtile, 7, FindNearIndustryName, &sni)) {
00279     /* An industry has been found nearby */
00280     IndustryType indtype = GetIndustryType(indtile);
00281     const IndustrySpec *indsp = GetIndustrySpec(indtype);
00282     /* STR_NULL means it only disables oil rig/mines */
00283     if (indsp->station_name != STR_NULL) {
00284       st->indtype = indtype;
00285       return STR_SV_STNAME_FALLBACK;
00286     }
00287   }
00288 
00289   /* Oil rigs/mines name could be marked not free by looking for a near by industry. */
00290   free_names = sni.free_names;
00291 
00292   /* check default names */
00293   uint32 tmp = free_names & _gen_station_name_bits[name_class];
00294   if (tmp != 0) return STR_SV_STNAME + FindFirstBit(tmp);
00295 
00296   /* check mine? */
00297   if (HasBit(free_names, M(STR_SV_STNAME_MINES))) {
00298     if (CountMapSquareAround(tile, CMSAMine) >= 2) {
00299       return STR_SV_STNAME_MINES;
00300     }
00301   }
00302 
00303   /* check close enough to town to get central as name? */
00304   if (DistanceMax(tile, t->xy) < 8) {
00305     if (HasBit(free_names, M(STR_SV_STNAME))) return STR_SV_STNAME;
00306 
00307     if (HasBit(free_names, M(STR_SV_STNAME_CENTRAL))) return STR_SV_STNAME_CENTRAL;
00308   }
00309 
00310   /* Check lakeside */
00311   if (HasBit(free_names, M(STR_SV_STNAME_LAKESIDE)) &&
00312       DistanceFromEdge(tile) < 20 &&
00313       CountMapSquareAround(tile, CMSAWater) >= 5) {
00314     return STR_SV_STNAME_LAKESIDE;
00315   }
00316 
00317   /* Check woods */
00318   if (HasBit(free_names, M(STR_SV_STNAME_WOODS)) && (
00319         CountMapSquareAround(tile, CMSATree) >= 8 ||
00320         CountMapSquareAround(tile, CMSAForest) >= 2)
00321       ) {
00322     return _settings_game.game_creation.landscape == LT_TROPIC ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
00323   }
00324 
00325   /* check elevation compared to town */
00326   uint z = GetTileZ(tile);
00327   uint z2 = GetTileZ(t->xy);
00328   if (z < z2) {
00329     if (HasBit(free_names, M(STR_SV_STNAME_VALLEY))) return STR_SV_STNAME_VALLEY;
00330   } else if (z > z2) {
00331     if (HasBit(free_names, M(STR_SV_STNAME_HEIGHTS))) return STR_SV_STNAME_HEIGHTS;
00332   }
00333 
00334   /* check direction compared to town */
00335   static const int8 _direction_and_table[] = {
00336     ~( (1 << M(STR_SV_STNAME_WEST))  | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00337     ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00338     ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
00339     ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) ),
00340   };
00341 
00342   free_names &= _direction_and_table[
00343     (TileX(tile) < TileX(t->xy)) +
00344     (TileY(tile) < TileY(t->xy)) * 2];
00345 
00346   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));
00347   return (tmp == 0) ? STR_SV_STNAME_FALLBACK : (STR_SV_STNAME + FindFirstBit(tmp));
00348 }
00349 #undef M
00350 
00356 static Station *GetClosestDeletedStation(TileIndex tile)
00357 {
00358   uint threshold = 8;
00359   Station *best_station = NULL;
00360   Station *st;
00361 
00362   FOR_ALL_STATIONS(st) {
00363     if (!st->IsInUse() && st->owner == _current_company) {
00364       uint cur_dist = DistanceManhattan(tile, st->xy);
00365 
00366       if (cur_dist < threshold) {
00367         threshold = cur_dist;
00368         best_station = st;
00369       }
00370     }
00371   }
00372 
00373   return best_station;
00374 }
00375 
00376 
00377 void Station::GetTileArea(TileArea *ta, StationType type) const
00378 {
00379   switch (type) {
00380     case STATION_RAIL:
00381       *ta = this->train_station;
00382       return;
00383 
00384     case STATION_AIRPORT:
00385       ta->tile = this->airport_tile;
00386       ta->w    = this->GetAirportSpec()->size_x;
00387       ta->h    = this->GetAirportSpec()->size_y;
00388       return;
00389 
00390     case STATION_TRUCK:
00391       *ta = this->truck_station;
00392       return;
00393 
00394     case STATION_BUS:
00395       *ta = this->bus_station;
00396       return;
00397 
00398     case STATION_DOCK:
00399     case STATION_OILRIG:
00400       ta->tile = this->dock_tile;
00401       break;
00402 
00403     default: NOT_REACHED();
00404   }
00405 
00406   ta->w = 1;
00407   ta->h = 1;
00408 }
00409 
00413 void Station::UpdateVirtCoord()
00414 {
00415   Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE);
00416 
00417   pt.y -= 32;
00418   if ((this->facilities & FACIL_AIRPORT) && this->airport_type == AT_OILRIG) pt.y -= 16;
00419 
00420   SetDParam(0, this->index);
00421   SetDParam(1, this->facilities);
00422   this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_STATION);
00423 
00424   SetWindowDirty(WC_STATION_VIEW, this->index);
00425 }
00426 
00428 void UpdateAllStationVirtCoords()
00429 {
00430   BaseStation *st;
00431 
00432   FOR_ALL_BASE_STATIONS(st) {
00433     st->UpdateVirtCoord();
00434   }
00435 }
00436 
00441 static uint GetAcceptanceMask(const Station *st)
00442 {
00443   uint mask = 0;
00444 
00445   for (CargoID i = 0; i < NUM_CARGO; i++) {
00446     if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) mask |= 1 << i;
00447   }
00448   return mask;
00449 }
00450 
00454 static void ShowRejectOrAcceptNews(const Station *st, uint num_items, CargoID *cargo, StringID msg)
00455 {
00456   for (uint i = 0; i < num_items; i++) {
00457     SetDParam(i + 1, CargoSpec::Get(cargo[i])->name);
00458   }
00459 
00460   SetDParam(0, st->index);
00461   AddNewsItem(msg, NS_ACCEPTANCE, NR_STATION, st->index);
00462 }
00463 
00471 CargoArray GetProductionAroundTiles(TileIndex tile, int w, int h, int rad)
00472 {
00473   CargoArray produced;
00474 
00475   int x = TileX(tile);
00476   int y = TileY(tile);
00477 
00478   /* expand the region by rad tiles on each side
00479    * while making sure that we remain inside the board. */
00480   int x2 = min(x + w + rad, MapSizeX());
00481   int x1 = max(x - rad, 0);
00482 
00483   int y2 = min(y + h + rad, MapSizeY());
00484   int y1 = max(y - rad, 0);
00485 
00486   assert(x1 < x2);
00487   assert(y1 < y2);
00488   assert(w > 0);
00489   assert(h > 0);
00490 
00491   TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
00492 
00493   /* Loop over all tiles to get the produced cargo of
00494    * everything except industries */
00495   TILE_AREA_LOOP(tile, ta) AddProducedCargo(tile, produced);
00496 
00497   /* Loop over the industries. They produce cargo for
00498    * anything that is within 'rad' from their bounding
00499    * box. As such if you have e.g. a oil well the tile
00500    * area loop might not hit an industry tile while
00501    * the industry would produce cargo for the station.
00502    */
00503   const Industry *i;
00504   FOR_ALL_INDUSTRIES(i) {
00505     if (!ta.Intersects(i->location)) continue;
00506 
00507     for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
00508       CargoID cargo = i->produced_cargo[j];
00509       if (cargo != CT_INVALID) produced[cargo]++;
00510     }
00511   }
00512 
00513   return produced;
00514 }
00515 
00524 CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, uint32 *always_accepted)
00525 {
00526   CargoArray acceptance;
00527   if (always_accepted != NULL) *always_accepted = 0;
00528 
00529   int x = TileX(tile);
00530   int y = TileY(tile);
00531 
00532   /* expand the region by rad tiles on each side
00533    * while making sure that we remain inside the board. */
00534   int x2 = min(x + w + rad, MapSizeX());
00535   int y2 = min(y + h + rad, MapSizeY());
00536   int x1 = max(x - rad, 0);
00537   int y1 = max(y - rad, 0);
00538 
00539   assert(x1 < x2);
00540   assert(y1 < y2);
00541   assert(w > 0);
00542   assert(h > 0);
00543 
00544   for (int yc = y1; yc != y2; yc++) {
00545     for (int xc = x1; xc != x2; xc++) {
00546       TileIndex tile = TileXY(xc, yc);
00547       AddAcceptedCargo(tile, acceptance, always_accepted);
00548     }
00549   }
00550 
00551   return acceptance;
00552 }
00553 
00558 void UpdateStationAcceptance(Station *st, bool show_msg)
00559 {
00560   /* old accepted goods types */
00561   uint old_acc = GetAcceptanceMask(st);
00562 
00563   /* And retrieve the acceptance. */
00564   CargoArray acceptance;
00565   if (!st->rect.IsEmpty()) {
00566     acceptance = GetAcceptanceAroundTiles(
00567       TileXY(st->rect.left, st->rect.top),
00568       st->rect.right  - st->rect.left + 1,
00569       st->rect.bottom - st->rect.top  + 1,
00570       st->GetCatchmentRadius(),
00571       &st->always_accepted
00572     );
00573   }
00574 
00575   /* Adjust in case our station only accepts fewer kinds of goods */
00576   for (CargoID i = 0; i < NUM_CARGO; i++) {
00577     uint amt = min(acceptance[i], 15);
00578 
00579     /* Make sure the station can accept the goods type. */
00580     bool is_passengers = IsCargoInClass(i, CC_PASSENGERS);
00581     if ((!is_passengers && !(st->facilities & ~FACIL_BUS_STOP)) ||
00582         (is_passengers && !(st->facilities & ~FACIL_TRUCK_STOP))) {
00583       amt = 0;
00584     }
00585 
00586     SB(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, amt >= 8);
00587   }
00588 
00589   /* Only show a message in case the acceptance was actually changed. */
00590   uint new_acc = GetAcceptanceMask(st);
00591   if (old_acc == new_acc) return;
00592 
00593   /* show a message to report that the acceptance was changed? */
00594   if (show_msg && st->owner == _local_company && st->IsInUse()) {
00595     /* List of accept and reject strings for different number of
00596      * cargo types */
00597     static const StringID accept_msg[] = {
00598       STR_NEWS_STATION_NOW_ACCEPTS_CARGO,
00599       STR_NEWS_STATION_NOW_ACCEPTS_CARGO_AND_CARGO,
00600     };
00601     static const StringID reject_msg[] = {
00602       STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO,
00603       STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_OR_CARGO,
00604     };
00605 
00606     /* Array of accepted and rejected cargo types */
00607     CargoID accepts[2] = { CT_INVALID, CT_INVALID };
00608     CargoID rejects[2] = { CT_INVALID, CT_INVALID };
00609     uint num_acc = 0;
00610     uint num_rej = 0;
00611 
00612     /* Test each cargo type to see if its acceptange has changed */
00613     for (CargoID i = 0; i < NUM_CARGO; i++) {
00614       if (HasBit(new_acc, i)) {
00615         if (!HasBit(old_acc, i) && num_acc < lengthof(accepts)) {
00616           /* New cargo is accepted */
00617           accepts[num_acc++] = i;
00618         }
00619       } else {
00620         if (HasBit(old_acc, i) && num_rej < lengthof(rejects)) {
00621           /* Old cargo is no longer accepted */
00622           rejects[num_rej++] = i;
00623         }
00624       }
00625     }
00626 
00627     /* Show news message if there are any changes */
00628     if (num_acc > 0) ShowRejectOrAcceptNews(st, num_acc, accepts, accept_msg[num_acc - 1]);
00629     if (num_rej > 0) ShowRejectOrAcceptNews(st, num_rej, rejects, reject_msg[num_rej - 1]);
00630   }
00631 
00632   /* redraw the station view since acceptance changed */
00633   SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_ACCEPTLIST);
00634 }
00635 
00636 static void UpdateStationSignCoord(BaseStation *st)
00637 {
00638   const StationRect *r = &st->rect;
00639 
00640   if (r->IsEmpty()) return; // no tiles belong to this station
00641 
00642   /* clamp sign coord to be inside the station rect */
00643   st->xy = TileXY(ClampU(TileX(st->xy), r->left, r->right), ClampU(TileY(st->xy), r->top, r->bottom));
00644   st->UpdateVirtCoord();
00645 }
00646 
00652 static void DeleteStationIfEmpty(BaseStation *st)
00653 {
00654   if (!st->IsInUse()) {
00655     st->delete_ctr = 0;
00656     InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
00657   }
00658   /* station remains but it probably lost some parts - station sign should stay in the station boundaries */
00659   UpdateStationSignCoord(st);
00660 }
00661 
00662 CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags);
00663 
00675 CommandCost CheckFlatLandBelow(TileIndex tile, uint w, uint h, DoCommandFlag flags, uint invalid_dirs, StationID *station, bool check_clear = true, RailType rt = INVALID_RAILTYPE, SmallVector<Train *, 4> *affected_vehicles = NULL)
00676 {
00677   CommandCost cost(EXPENSES_CONSTRUCTION);
00678   int allowed_z = -1;
00679 
00680   TILE_LOOP(tile_cur, w, h, tile) {
00681     if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) {
00682       return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00683     }
00684 
00685     if (!EnsureNoVehicleOnGround(tile_cur)) return CMD_ERROR;
00686 
00687     uint z;
00688     Slope tileh = GetTileSlope(tile_cur, &z);
00689 
00690     /* Prohibit building if
00691      *   1) The tile is "steep" (i.e. stretches two height levels)
00692      *   2) The tile is non-flat and the build_on_slopes switch is disabled
00693      */
00694     if (IsSteepSlope(tileh) ||
00695         ((!_settings_game.construction.build_on_slopes) && tileh != SLOPE_FLAT)) {
00696       return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
00697     }
00698 
00699     int flat_z = z;
00700     if (tileh != SLOPE_FLAT) {
00701       /* need to check so the entrance to the station is not pointing at a slope.
00702        * This must be valid for all station tiles, as the user can remove single station tiles. */
00703       if ((HasBit(invalid_dirs, DIAGDIR_NE) && !(tileh & SLOPE_NE)) ||
00704           (HasBit(invalid_dirs, DIAGDIR_SE) && !(tileh & SLOPE_SE)) ||
00705           (HasBit(invalid_dirs, DIAGDIR_SW) && !(tileh & SLOPE_SW)) ||
00706           (HasBit(invalid_dirs, DIAGDIR_NW) && !(tileh & SLOPE_NW))) {
00707         return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
00708       }
00709       cost.AddCost(_price[PR_BUILD_FOUNDATION]);
00710       flat_z += TILE_HEIGHT;
00711     }
00712 
00713     /* get corresponding flat level and make sure that all parts of the station have the same level. */
00714     if (allowed_z == -1) {
00715       /* first tile */
00716       allowed_z = flat_z;
00717     } else if (allowed_z != flat_z) {
00718       return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
00719     }
00720 
00721     /* if station is set, then we have special handling to allow building on top of already existing stations.
00722      * so station points to INVALID_STATION if we can build on any station.
00723      * Or it points to a station if we're only allowed to build on exactly that station. */
00724     if (station != NULL && IsTileType(tile_cur, MP_STATION)) {
00725       if (!IsRailStation(tile_cur)) {
00726         return ClearTile_Station(tile_cur, DC_AUTO); // get error message
00727       } else {
00728         StationID st = GetStationIndex(tile_cur);
00729         if (*station == INVALID_STATION) {
00730           *station = st;
00731         } else if (*station != st) {
00732           return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
00733         }
00734       }
00735     } else if (check_clear) {
00736       /* Rail type is only valid when building a railway station; in station to
00737        * build isn't a rail station it's INVALID_RAILTYPE. */
00738       if (rt != INVALID_RAILTYPE &&
00739           IsPlainRailTile(tile_cur) && !HasSignals(tile_cur) &&
00740           HasPowerOnRail(GetRailType(tile_cur), rt)) {
00741         /* Allow overbuilding if the tile:
00742          *  - has rail, but no signals
00743          *  - it has exactly one track
00744          *  - the track is in line with the station
00745          *  - the current rail type has power on the to-be-built type (e.g. convert normal rail to el rail)
00746          */
00747         TrackBits tracks = GetTrackBits(tile_cur);
00748         Track track = RemoveFirstTrack(&tracks);
00749         Track expected_track = HasBit(invalid_dirs, DIAGDIR_NE) ? TRACK_X : TRACK_Y;
00750 
00751         if (tracks == TRACK_BIT_NONE && track == expected_track) {
00752           /* Check for trains having a reservation for this tile. */
00753           if (HasBit(GetRailReservationTrackBits(tile_cur), track)) {
00754             Train *v = GetTrainForReservation(tile_cur, track);
00755             if (v != NULL && affected_vehicles != NULL) {
00756               *(*affected_vehicles).Append() = v;
00757             }
00758           }
00759           CommandCost ret = DoCommand(tile_cur, 0, track, flags, CMD_REMOVE_SINGLE_RAIL);
00760           if (ret.Failed()) return ret;
00761           cost.AddCost(ret);
00762           /* With flags & ~DC_EXEC CmdLandscapeClear would fail since the rail still exists */
00763           continue;
00764         }
00765       }
00766       CommandCost ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00767       if (ret.Failed()) return ret;
00768       cost.AddCost(ret);
00769     }
00770   }
00771 
00772   return cost;
00773 }
00774 
00782 bool CanExpandRailStation(const BaseStation *st, TileArea &new_ta, Axis axis)
00783 {
00784   TileArea cur_ta = st->train_station;
00785 
00786   if (_settings_game.station.nonuniform_stations) {
00787     /* determine new size of train station region.. */
00788     int x = min(TileX(cur_ta.tile), TileX(new_ta.tile));
00789     int y = min(TileY(cur_ta.tile), TileY(new_ta.tile));
00790     new_ta.w = max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
00791     new_ta.h = max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
00792     new_ta.tile = TileXY(x, y);
00793   } else {
00794     /* do not allow modifying non-uniform stations,
00795      * the uniform-stations code wouldn't handle it well */
00796     TILE_LOOP(t, cur_ta.w, cur_ta.h, cur_ta.tile) {
00797       if (!st->TileBelongsToRailStation(t)) { // there may be adjoined station
00798         _error_message = STR_ERROR_NONUNIFORM_STATIONS_DISALLOWED;
00799         return false;
00800       }
00801     }
00802 
00803     /* check so the orientation is the same */
00804     if (GetRailStationAxis(cur_ta.tile) != axis) {
00805       _error_message = STR_ERROR_NONUNIFORM_STATIONS_DISALLOWED;
00806       return false;
00807     }
00808 
00809     /* check if the new station adjoins the old station in either direction */
00810     if (cur_ta.w == new_ta.w && cur_ta.tile == new_ta.tile + TileDiffXY(0, new_ta.h)) {
00811       /* above */
00812       new_ta.h += cur_ta.h;
00813     } else if (cur_ta.w == new_ta.w && cur_ta.tile == new_ta.tile - TileDiffXY(0, cur_ta.h)) {
00814       /* below */
00815       new_ta.tile = cur_ta.tile;
00816       new_ta.h += new_ta.h;
00817     } else if (cur_ta.h == new_ta.h && cur_ta.tile == new_ta.tile + TileDiffXY(new_ta.w, 0)) {
00818       /* to the left */
00819       new_ta.w += cur_ta.w;
00820     } else if (cur_ta.h == new_ta.h && cur_ta.tile == new_ta.tile - TileDiffXY(cur_ta.w, 0)) {
00821       /* to the right */
00822       new_ta.tile = cur_ta.tile;
00823       new_ta.w += cur_ta.w;
00824     } else {
00825       _error_message = STR_ERROR_NONUNIFORM_STATIONS_DISALLOWED;
00826       return false;
00827     }
00828   }
00829   /* make sure the final size is not too big. */
00830   if (new_ta.w > _settings_game.station.station_spread || new_ta.h > _settings_game.station.station_spread) {
00831     _error_message = STR_ERROR_STATION_TOO_SPREAD_OUT;
00832     return false;
00833   }
00834 
00835   return true;
00836 }
00837 
00838 static inline byte *CreateSingle(byte *layout, int n)
00839 {
00840   int i = n;
00841   do *layout++ = 0; while (--i);
00842   layout[((n - 1) >> 1) - n] = 2;
00843   return layout;
00844 }
00845 
00846 static inline byte *CreateMulti(byte *layout, int n, byte b)
00847 {
00848   int i = n;
00849   do *layout++ = b; while (--i);
00850   if (n > 4) {
00851     layout[0 - n] = 0;
00852     layout[n - 1 - n] = 0;
00853   }
00854   return layout;
00855 }
00856 
00857 void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec)
00858 {
00859   if (statspec != NULL && statspec->lengths >= plat_len &&
00860       statspec->platforms[plat_len - 1] >= numtracks &&
00861       statspec->layouts[plat_len - 1][numtracks - 1]) {
00862     /* Custom layout defined, follow it. */
00863     memcpy(layout, statspec->layouts[plat_len - 1][numtracks - 1],
00864       plat_len * numtracks);
00865     return;
00866   }
00867 
00868   if (plat_len == 1) {
00869     CreateSingle(layout, numtracks);
00870   } else {
00871     if (numtracks & 1) layout = CreateSingle(layout, plat_len);
00872     numtracks >>= 1;
00873 
00874     while (--numtracks >= 0) {
00875       layout = CreateMulti(layout, plat_len, 4);
00876       layout = CreateMulti(layout, plat_len, 6);
00877     }
00878   }
00879 }
00880 
00892 template <class T, StringID error_message>
00893 CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st)
00894 {
00895   assert(*st == NULL);
00896   bool check_surrounding = true;
00897 
00898   if (_settings_game.station.adjacent_stations) {
00899     if (existing_station != INVALID_STATION) {
00900       if (adjacent && existing_station != station_to_join) {
00901         /* You can't build an adjacent station over the top of one that
00902          * already exists. */
00903         return_cmd_error(error_message);
00904       } else {
00905         /* Extend the current station, and don't check whether it will
00906          * be near any other stations. */
00907         *st = T::GetIfValid(existing_station);
00908         check_surrounding = (*st == NULL);
00909       }
00910     } else {
00911       /* There's no station here. Don't check the tiles surrounding this
00912        * one if the company wanted to build an adjacent station. */
00913       if (adjacent) check_surrounding = false;
00914     }
00915   }
00916 
00917   if (check_surrounding) {
00918     /* Make sure there are no similar stations around us. */
00919     if (!GetStationAround(ta, existing_station, st)) return CMD_ERROR;
00920   }
00921 
00922   /* Distant join */
00923   if (*st == NULL && station_to_join != INVALID_STATION) *st = T::GetIfValid(station_to_join);
00924 
00925   return CommandCost();
00926 }
00927 
00937 static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
00938 {
00939   return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st);
00940 }
00941 
00951 CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp)
00952 {
00953   return FindJoiningBaseStation<Waypoint, STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp);
00954 }
00955 
00973 CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00974 {
00975   /* Unpack parameters */
00976   RailType rt    = Extract<RailType, 0, 4>(p1);
00977   Axis axis      = Extract<Axis, 4, 1>(p1);
00978   byte numtracks = GB(p1,  8, 8);
00979   byte plat_len  = GB(p1, 16, 8);
00980   bool adjacent  = HasBit(p1, 24);
00981 
00982   StationClassID spec_class = Extract<StationClassID, 0, 8>(p2);
00983   byte spec_index           = GB(p2, 8, 8);
00984   StationID station_to_join = GB(p2, 16, 16);
00985 
00986   /* Does the authority allow this? */
00987   if (!CheckIfAuthorityAllowsNewStation(tile_org, flags)) return CMD_ERROR;
00988   if (!ValParamRailtype(rt)) return CMD_ERROR;
00989 
00990   /* Check if the given station class is valid */
00991   if ((uint)spec_class >= GetNumStationClasses() || spec_class == STAT_CLASS_WAYP) return CMD_ERROR;
00992   if (spec_index >= GetNumCustomStations(spec_class)) return CMD_ERROR;
00993   if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
00994 
00995   int w_org, h_org;
00996   if (axis == AXIS_X) {
00997     w_org = plat_len;
00998     h_org = numtracks;
00999   } else {
01000     h_org = plat_len;
01001     w_org = numtracks;
01002   }
01003 
01004   bool reuse = (station_to_join != NEW_STATION);
01005   if (!reuse) station_to_join = INVALID_STATION;
01006   bool distant_join = (station_to_join != INVALID_STATION);
01007 
01008   if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
01009 
01010   if (h_org > _settings_game.station.station_spread || w_org > _settings_game.station.station_spread) return CMD_ERROR;
01011 
01012   /* these values are those that will be stored in train_tile and station_platforms */
01013   TileArea new_location(tile_org, w_org, h_org);
01014 
01015   /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
01016   StationID est = INVALID_STATION;
01017   SmallVector<Train *, 4> affected_vehicles;
01018   /* Clear the land below the station. */
01019   CommandCost cost = CheckFlatLandBelow(tile_org, w_org, h_org, flags, 5 << axis, _settings_game.station.nonuniform_stations ? &est : NULL, true, rt, &affected_vehicles);
01020   if (cost.Failed()) return cost;
01021   /* Add construction expenses. */
01022   cost.AddCost((numtracks * _price[PR_BUILD_STATION_RAIL] + _price[PR_BUILD_STATION_RAIL_LENGTH]) * plat_len);
01023 
01024   Station *st = NULL;
01025   CommandCost ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
01026   if (ret.Failed()) return ret;
01027 
01028   /* See if there is a deleted station close to us. */
01029   if (st == NULL && reuse) st = GetClosestDeletedStation(tile_org);
01030 
01031   if (st != NULL) {
01032     /* Reuse an existing station. */
01033     if (st->owner != _current_company)
01034       return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
01035 
01036     if (st->train_station.tile != INVALID_TILE) {
01037       /* check if we want to expanding an already existing station? */
01038       if (!_settings_game.station.join_stations)
01039         return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_RAILROAD);
01040       if (!CanExpandRailStation(st, new_location, axis))
01041         return CMD_ERROR;
01042     }
01043 
01044     /* XXX can't we pack this in the "else" part of the if above? */
01045     if (!st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TEST)) return CMD_ERROR;
01046   } else {
01047     /* allocate and initialize new station */
01048     if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
01049 
01050     if (flags & DC_EXEC) {
01051       st = new Station(tile_org);
01052 
01053       st->town = ClosestTownFromTile(tile_org, UINT_MAX);
01054       st->string_id = GenerateStationName(st, tile_org, STATIONNAMING_RAIL);
01055 
01056       if (Company::IsValidID(_current_company)) {
01057         SetBit(st->town->have_ratings, _current_company);
01058       }
01059     }
01060   }
01061 
01062   /* Check if we can allocate a custom stationspec to this station */
01063   const StationSpec *statspec = GetCustomStationSpec(spec_class, spec_index);
01064   int specindex = AllocateSpecToStation(statspec, st, (flags & DC_EXEC) != 0);
01065   if (specindex == -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS);
01066 
01067   if (statspec != NULL) {
01068     /* Perform NewStation checks */
01069 
01070     /* Check if the station size is permitted */
01071     if (HasBit(statspec->disallowed_platforms, numtracks - 1) || HasBit(statspec->disallowed_lengths, plat_len - 1)) {
01072       return CMD_ERROR;
01073     }
01074 
01075     /* Check if the station is buildable */
01076     if (HasBit(statspec->callback_mask, CBM_STATION_AVAIL) && GB(GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE), 0, 8) == 0) {
01077       return CMD_ERROR;
01078     }
01079   }
01080 
01081   if (flags & DC_EXEC) {
01082     TileIndexDiff tile_delta;
01083     byte *layout_ptr;
01084     byte numtracks_orig;
01085     Track track;
01086 
01087     st->train_station = new_location;
01088     st->AddFacility(FACIL_TRAIN, new_location.tile);
01089 
01090     st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
01091 
01092     if (statspec != NULL) {
01093       /* Include this station spec's animation trigger bitmask
01094        * in the station's cached copy. */
01095       st->cached_anim_triggers |= statspec->anim_triggers;
01096     }
01097 
01098     tile_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
01099     track = AxisToTrack(axis);
01100 
01101     layout_ptr = AllocaM(byte, numtracks * plat_len);
01102     GetStationLayout(layout_ptr, numtracks, plat_len, statspec);
01103 
01104     numtracks_orig = numtracks;
01105 
01106     do {
01107       TileIndex tile = tile_org;
01108       int w = plat_len;
01109       do {
01110         byte layout = *layout_ptr++;
01111         if (IsRailStationTile(tile) && HasStationReservation(tile)) {
01112           /* Check for trains having a reservation for this tile. */
01113           Train *v = GetTrainForReservation(tile, AxisToTrack(GetRailStationAxis(tile)));
01114           if (v != NULL) {
01115             FreeTrainTrackReservation(v);
01116             *affected_vehicles.Append() = v;
01117             if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), false);
01118             for (; v->Next() != NULL; v = v->Next()) { }
01119             if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), false);
01120           }
01121         }
01122 
01123         /* Remove animation if overbuilding */
01124         DeleteAnimatedTile(tile);
01125         byte old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
01126         MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, rt);
01127         /* Free the spec if we overbuild something */
01128         DeallocateSpecFromStation(st, old_specindex);
01129 
01130         SetCustomStationSpecIndex(tile, specindex);
01131         SetStationTileRandomBits(tile, GB(Random(), 0, 4));
01132         SetStationAnimationFrame(tile, 0);
01133 
01134         if (statspec != NULL) {
01135           /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
01136           uint32 platinfo = GetPlatformInfo(AXIS_X, 0, plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
01137 
01138           /* As the station is not yet completely finished, the station does not yet exist. */
01139           uint16 callback = GetStationCallback(CBID_STATION_TILE_LAYOUT, platinfo, 0, statspec, NULL, tile);
01140           if (callback != CALLBACK_FAILED && callback < 8) SetStationGfx(tile, (callback & ~1) + axis);
01141 
01142           /* Trigger station animation -- after building? */
01143           StationAnimationTrigger(st, tile, STAT_ANIM_BUILT);
01144         }
01145 
01146         tile += tile_delta;
01147       } while (--w);
01148       AddTrackToSignalBuffer(tile_org, track, _current_company);
01149       YapfNotifyTrackLayoutChange(tile_org, track);
01150       tile_org += tile_delta ^ TileDiffXY(1, 1); // perpendicular to tile_delta
01151     } while (--numtracks);
01152 
01153     for (uint i = 0; i < affected_vehicles.Length(); ++i) {
01154       /* Restore reservations of trains. */
01155       Train *v = affected_vehicles[i];
01156       if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true);
01157       TryPathReserve(v, true, true);
01158       for (; v->Next() != NULL; v = v->Next()) { }
01159       if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), true);
01160     }
01161 
01162     st->MarkTilesDirty(false);
01163     st->UpdateVirtCoord();
01164     UpdateStationAcceptance(st, false);
01165     st->RecomputeIndustriesNear();
01166     InvalidateWindowData(WC_SELECT_STATION, 0, 0);
01167     InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
01168     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
01169   }
01170 
01171   return cost;
01172 }
01173 
01174 static void MakeRailStationAreaSmaller(BaseStation *st)
01175 {
01176   TileArea ta = st->train_station;
01177 
01178 restart:
01179 
01180   /* too small? */
01181   if (ta.w != 0 && ta.h != 0) {
01182     /* check the left side, x = constant, y changes */
01183     for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(0, i));) {
01184       /* the left side is unused? */
01185       if (++i == ta.h) {
01186         ta.tile += TileDiffXY(1, 0);
01187         ta.w--;
01188         goto restart;
01189       }
01190     }
01191 
01192     /* check the right side, x = constant, y changes */
01193     for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(ta.w - 1, i));) {
01194       /* the right side is unused? */
01195       if (++i == ta.h) {
01196         ta.w--;
01197         goto restart;
01198       }
01199     }
01200 
01201     /* check the upper side, y = constant, x changes */
01202     for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(i, 0));) {
01203       /* the left side is unused? */
01204       if (++i == ta.w) {
01205         ta.tile += TileDiffXY(0, 1);
01206         ta.h--;
01207         goto restart;
01208       }
01209     }
01210 
01211     /* check the lower side, y = constant, x changes */
01212     for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(i, ta.h - 1));) {
01213       /* the left side is unused? */
01214       if (++i == ta.w) {
01215         ta.h--;
01216         goto restart;
01217       }
01218     }
01219   } else {
01220     ta.Clear();
01221   }
01222 
01223   st->train_station = ta;
01224 }
01225 
01236 template <class T>
01237 CommandCost RemoveFromRailBaseStation(TileArea ta, SmallVector<T *, 4> &affected_stations, DoCommandFlag flags, Money removal_cost, bool keep_rail)
01238 {
01239   /* Count of the number of tiles removed */
01240   int quantity = 0;
01241   CommandCost total_cost(EXPENSES_CONSTRUCTION);
01242 
01243   /* Do the action for every tile into the area */
01244   TILE_AREA_LOOP(tile, ta) {
01245     /* Make sure the specified tile is a rail station */
01246     if (!HasStationTileRail(tile)) continue;
01247 
01248     /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
01249     if (!EnsureNoVehicleOnGround(tile)) continue;
01250 
01251     /* Check ownership of station */
01252     T *st = T::GetByTile(tile);
01253     if (st == NULL) continue;
01254     if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) continue;
01255 
01256     /* Do not allow removing from stations if non-uniform stations are not enabled
01257      * The check must be here to give correct error message
01258      */
01259     if (!_settings_game.station.nonuniform_stations) return_cmd_error(STR_ERROR_NONUNIFORM_STATIONS_DISALLOWED);
01260 
01261     /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
01262     quantity++;
01263 
01264     if (flags & DC_EXEC) {
01265       /* read variables before the station tile is removed */
01266       uint specindex = GetCustomStationSpecIndex(tile);
01267       Track track = GetRailStationTrack(tile);
01268       Owner owner = GetTileOwner(tile);
01269       RailType rt = GetRailType(tile);
01270       Train *v = NULL;
01271 
01272       if (HasStationReservation(tile)) {
01273         v = GetTrainForReservation(tile, track);
01274         if (v != NULL) {
01275           /* Free train reservation. */
01276           FreeTrainTrackReservation(v);
01277           if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), false);
01278           Vehicle *temp = v;
01279           for (; temp->Next() != NULL; temp = temp->Next()) { }
01280           if (IsRailStationTile(temp->tile)) SetRailStationPlatformReservation(temp->tile, TrackdirToExitdir(ReverseTrackdir(temp->GetVehicleTrackdir())), false);
01281         }
01282       }
01283 
01284       DoClearSquare(tile);
01285       if (keep_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt);
01286 
01287       st->rect.AfterRemoveTile(st, tile);
01288       AddTrackToSignalBuffer(tile, track, owner);
01289       YapfNotifyTrackLayoutChange(tile, track);
01290 
01291       DeallocateSpecFromStation(st, specindex);
01292 
01293       affected_stations.Include(st);
01294 
01295       if (v != NULL) {
01296         /* Restore station reservation. */
01297         if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true);
01298         TryPathReserve(v, true, true);
01299         for (; v->Next() != NULL; v = v->Next()) { }
01300         if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), true);
01301       }
01302     }
01303     if (keep_rail) {
01304       /* Don't refund the 'steel' of the track! */
01305       total_cost.AddCost(-_price[PR_CLEAR_RAIL]);
01306     }
01307   }
01308 
01309   if (quantity == 0) return CMD_ERROR;
01310 
01311   for (T **stp = affected_stations.Begin(); stp != affected_stations.End(); stp++) {
01312     T *st = *stp;
01313 
01314     /* now we need to make the "spanned" area of the railway station smaller
01315      * if we deleted something at the edges.
01316      * we also need to adjust train_tile. */
01317     MakeRailStationAreaSmaller(st);
01318     UpdateStationSignCoord(st);
01319 
01320     /* if we deleted the whole station, delete the train facility. */
01321     if (st->train_station.tile == INVALID_TILE) {
01322       st->facilities &= ~FACIL_TRAIN;
01323       SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
01324       st->UpdateVirtCoord();
01325       DeleteStationIfEmpty(st);
01326     }
01327   }
01328 
01329   total_cost.AddCost(quantity * removal_cost);
01330   return total_cost;
01331 }
01332 
01343 CommandCost CmdRemoveFromRailStation(TileIndex start, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01344 {
01345   TileIndex end = p1 == 0 ? start : p1;
01346   if (start >= MapSize() || end >= MapSize()) return CMD_ERROR;
01347 
01348   TileArea ta(start, end);
01349   SmallVector<Station *, 4> affected_stations;
01350 
01351   CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_STATION_RAIL], HasBit(p2, 0));
01352   if (ret.Failed()) return ret;
01353 
01354   /* Do all station specific functions here. */
01355   for (Station **stp = affected_stations.Begin(); stp != affected_stations.End(); stp++) {
01356     Station *st = *stp;
01357 
01358     if (st->train_station.tile == INVALID_TILE) SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
01359     st->MarkTilesDirty(false);
01360     st->RecomputeIndustriesNear();
01361   }
01362 
01363   /* Now apply the rail cost to the number that we deleted */
01364   return ret;
01365 }
01366 
01377 CommandCost CmdRemoveFromRailWaypoint(TileIndex start, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01378 {
01379   TileIndex end = p1 == 0 ? start : p1;
01380   if (start >= MapSize() || end >= MapSize()) return CMD_ERROR;
01381 
01382   TileArea ta(start, end);
01383   SmallVector<Waypoint *, 4> affected_stations;
01384 
01385   return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_WAYPOINT_RAIL], HasBit(p2, 0));
01386 }
01387 
01388 
01396 template <class T>
01397 CommandCost RemoveRailStation(T *st, DoCommandFlag flags)
01398 {
01399   /* Current company owns the station? */
01400   if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) return CMD_ERROR;
01401 
01402   /* determine width and height of platforms */
01403   TileArea ta = st->train_station;
01404 
01405   assert(ta.w != 0 && ta.h != 0);
01406 
01407   CommandCost cost(EXPENSES_CONSTRUCTION);
01408   /* clear all areas of the station */
01409   TILE_AREA_LOOP(tile, ta) {
01410     /* for nonuniform stations, only remove tiles that are actually train station tiles */
01411     if (!st->TileBelongsToRailStation(tile)) continue;
01412 
01413     if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
01414 
01415     cost.AddCost(_price[PR_CLEAR_STATION_RAIL]);
01416     if (flags & DC_EXEC) {
01417       /* read variables before the station tile is removed */
01418       Track track = GetRailStationTrack(tile);
01419       Owner owner = GetTileOwner(tile); // _current_company can be OWNER_WATER
01420       Train *v = NULL;
01421       if (HasStationReservation(tile)) {
01422         v = GetTrainForReservation(tile, track);
01423         if (v != NULL) FreeTrainTrackReservation(v);
01424       }
01425       DoClearSquare(tile);
01426       AddTrackToSignalBuffer(tile, track, owner);
01427       YapfNotifyTrackLayoutChange(tile, track);
01428       if (v != NULL) TryPathReserve(v, true);
01429     }
01430   }
01431 
01432   if (flags & DC_EXEC) {
01433     st->rect.AfterRemoveRect(st, st->train_station.tile, st->train_station.w, st->train_station.h);
01434 
01435     st->train_station.Clear();
01436 
01437     st->facilities &= ~FACIL_TRAIN;
01438 
01439     free(st->speclist);
01440     st->num_specs = 0;
01441     st->speclist  = NULL;
01442     st->cached_anim_triggers = 0;
01443 
01444     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
01445     st->UpdateVirtCoord();
01446     DeleteStationIfEmpty(st);
01447   }
01448 
01449   return cost;
01450 }
01451 
01458 static CommandCost RemoveRailStation(TileIndex tile, DoCommandFlag flags)
01459 {
01460   /* if there is flooding and non-uniform stations are enabled, remove platforms tile by tile */
01461   if (_current_company == OWNER_WATER && _settings_game.station.nonuniform_stations) {
01462     return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAIL_STATION);
01463   }
01464 
01465   Station *st = Station::GetByTile(tile);
01466   CommandCost cost = RemoveRailStation(st, flags);
01467 
01468   if (flags & DC_EXEC) st->RecomputeIndustriesNear();
01469 
01470   return cost;
01471 }
01472 
01479 static CommandCost RemoveRailWaypoint(TileIndex tile, DoCommandFlag flags)
01480 {
01481   /* if there is flooding and non-uniform stations are enabled, remove waypoints tile by tile */
01482   if (_current_company == OWNER_WATER && _settings_game.station.nonuniform_stations) {
01483     return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAIL_WAYPOINT);
01484   }
01485 
01486   return RemoveRailStation(Waypoint::GetByTile(tile), flags);
01487 }
01488 
01489 
01495 static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
01496 {
01497   RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
01498 
01499   if (*primary_stop == NULL) {
01500     /* we have no roadstop of the type yet, so write a "primary stop" */
01501     return primary_stop;
01502   } else {
01503     /* there are stops already, so append to the end of the list */
01504     RoadStop *stop = *primary_stop;
01505     while (stop->next != NULL) stop = stop->next;
01506     return &stop->next;
01507   }
01508 }
01509 
01522 CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01523 {
01524   bool type = HasBit(p2, 0);
01525   bool is_drive_through = HasBit(p2, 1);
01526   bool build_over_road  = is_drive_through && IsNormalRoadTile(tile);
01527   RoadTypes rts = Extract<RoadTypes, 2, 2>(p2);
01528   StationID station_to_join = GB(p2, 16, 16);
01529   bool reuse = (station_to_join != NEW_STATION);
01530   if (!reuse) station_to_join = INVALID_STATION;
01531   bool distant_join = (station_to_join != INVALID_STATION);
01532   Owner tram_owner = _current_company;
01533   Owner road_owner = _current_company;
01534 
01535   if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
01536 
01537   if (!HasExactlyOneBit(rts) || !HasRoadTypesAvail(_current_company, rts)) return CMD_ERROR;
01538 
01539   /* Trams only have drive through stops */
01540   if (!is_drive_through && HasBit(rts, ROADTYPE_TRAM)) return CMD_ERROR;
01541 
01542   /* Saveguard the parameters */
01543   if (!IsValidDiagDirection((DiagDirection)p1)) return CMD_ERROR;
01544   /* If it is a drive-through stop check for valid axis */
01545   if (is_drive_through && !IsValidAxis((Axis)p1)) return CMD_ERROR;
01546   /* Road bits in the wrong direction */
01547   if (build_over_road && (GetAllRoadBits(tile) & ((Axis)p1 == AXIS_X ? ROAD_Y : ROAD_X)) != 0) return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
01548 
01549   if (!CheckIfAuthorityAllowsNewStation(tile, flags)) return CMD_ERROR;
01550 
01551   RoadTypes cur_rts = IsNormalRoadTile(tile) ? GetRoadTypes(tile) : ROADTYPES_NONE;
01552   uint num_roadbits = 0;
01553   /* Not allowed to build over this road */
01554   if (build_over_road) {
01555     /* there is a road, check if we can build road+tram stop over it */
01556     if (HasBit(cur_rts, ROADTYPE_ROAD)) {
01557       road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
01558       if (road_owner == OWNER_TOWN) {
01559         if (!_settings_game.construction.road_stop_on_town_road) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
01560       } else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE && !CheckOwnership(road_owner)) {
01561         return CMD_ERROR;
01562       }
01563       num_roadbits += CountBits(GetRoadBits(tile, ROADTYPE_ROAD));
01564     }
01565 
01566     /* there is a tram, check if we can build road+tram stop over it */
01567     if (HasBit(cur_rts, ROADTYPE_TRAM)) {
01568       tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
01569       if (!_settings_game.construction.road_stop_on_competitor_road && tram_owner != OWNER_NONE && !CheckOwnership(tram_owner)) {
01570         return CMD_ERROR;
01571       }
01572       num_roadbits += CountBits(GetRoadBits(tile, ROADTYPE_TRAM));
01573     }
01574 
01575     /* Don't allow building the roadstop when vehicles are already driving on it */
01576     if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
01577 
01578     /* Do not remove roadtypes! */
01579     rts |= cur_rts;
01580   }
01581 
01582   CommandCost cost = CheckFlatLandBelow(tile, 1, 1, flags, is_drive_through ? 5 << p1 : 1 << p1, NULL, !build_over_road);
01583   if (cost.Failed()) return cost;
01584   uint roadbits_to_build = CountBits(rts) * 2 - num_roadbits;
01585   cost.AddCost(_price[PR_BUILD_ROAD] * roadbits_to_build);
01586 
01587   Station *st = NULL;
01588   CommandCost ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p2, 5), TileArea(tile, 1, 1), &st);
01589   if (ret.Failed()) return ret;
01590 
01591   /* Find a deleted station close to us */
01592   if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
01593 
01594   /* give us a road stop in the list, and check if something went wrong */
01595   if (!RoadStop::CanAllocateItem()) return_cmd_error(type ? STR_ERROR_TOO_MANY_TRUCK_STOPS : STR_ERROR_TOO_MANY_BUS_STOPS);
01596 
01597   if (st != NULL) {
01598     if (st->owner != _current_company) {
01599       return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
01600     }
01601 
01602     if (!st->rect.BeforeAddTile(tile, StationRect::ADD_TEST)) return CMD_ERROR;
01603   } else {
01604     /* allocate and initialize new station */
01605     if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
01606 
01607     if (flags & DC_EXEC) {
01608       st = new Station(tile);
01609 
01610       st->town = ClosestTownFromTile(tile, UINT_MAX);
01611       st->string_id = GenerateStationName(st, tile, STATIONNAMING_ROAD);
01612 
01613       if (Company::IsValidID(_current_company)) {
01614         SetBit(st->town->have_ratings, _current_company);
01615       }
01616     }
01617   }
01618 
01619   cost.AddCost(_price[type ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]);
01620 
01621   if (flags & DC_EXEC) {
01622     RoadStop *road_stop = new RoadStop(tile);
01623     /* Insert into linked list of RoadStops */
01624     RoadStop **currstop = FindRoadStopSpot(type, st);
01625     *currstop = road_stop;
01626 
01627     if (type) {
01628       st->truck_station.Add(tile);
01629     } else {
01630       st->bus_station.Add(tile);
01631     }
01632 
01633     /* initialize an empty station */
01634     st->AddFacility((type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP, tile);
01635 
01636     st->rect.BeforeAddTile(tile, StationRect::ADD_TRY);
01637 
01638     RoadStopType rs_type = type ? ROADSTOP_TRUCK : ROADSTOP_BUS;
01639     if (is_drive_through) {
01640       MakeDriveThroughRoadStop(tile, st->owner, road_owner, tram_owner, st->index, rs_type, rts, (Axis)p1);
01641       road_stop->MakeDriveThrough();
01642     } else {
01643       MakeRoadStop(tile, st->owner, st->index, rs_type, rts, (DiagDirection)p1);
01644     }
01645 
01646     st->UpdateVirtCoord();
01647     UpdateStationAcceptance(st, false);
01648     st->RecomputeIndustriesNear();
01649     InvalidateWindowData(WC_SELECT_STATION, 0, 0);
01650     InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
01651     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_ROADVEHS);
01652   }
01653   return cost;
01654 }
01655 
01656 
01657 static Vehicle *ClearRoadStopStatusEnum(Vehicle *v, void *)
01658 {
01659   if (v->type == VEH_ROAD) {
01660     /* Okay... we are a road vehicle on a drive through road stop.
01661      * But that road stop has just been removed, so we need to make
01662      * sure we are in a valid state... however, vehicles can also
01663      * turn on road stop tiles, so only clear the 'road stop' state
01664      * bits and only when the state was 'in road stop', otherwise
01665      * we'll end up clearing the turn around bits. */
01666     RoadVehicle *rv = RoadVehicle::From(v);
01667     if (HasBit(rv->state, RVS_IN_DT_ROAD_STOP)) rv->state &= RVSB_ROAD_STOP_TRACKDIR_MASK;
01668   }
01669 
01670   return NULL;
01671 }
01672 
01673 
01680 static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
01681 {
01682   Station *st = Station::GetByTile(tile);
01683 
01684   if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) {
01685     return CMD_ERROR;
01686   }
01687 
01688   bool is_truck = IsTruckStop(tile);
01689 
01690   RoadStop **primary_stop;
01691   RoadStop *cur_stop;
01692   if (is_truck) { // truck stop
01693     primary_stop = &st->truck_stops;
01694     cur_stop = RoadStop::GetByTile(tile, ROADSTOP_TRUCK);
01695   } else {
01696     primary_stop = &st->bus_stops;
01697     cur_stop = RoadStop::GetByTile(tile, ROADSTOP_BUS);
01698   }
01699 
01700   assert(cur_stop != NULL);
01701 
01702   /* don't do the check for drive-through road stops when company bankrupts */
01703   if (IsDriveThroughStopTile(tile) && (flags & DC_BANKRUPT)) {
01704     /* remove the 'going through road stop' status from all vehicles on that tile */
01705     if (flags & DC_EXEC) FindVehicleOnPos(tile, NULL, &ClearRoadStopStatusEnum);
01706   } else {
01707     if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
01708   }
01709 
01710   if (flags & DC_EXEC) {
01711     if (*primary_stop == cur_stop) {
01712       /* removed the first stop in the list */
01713       *primary_stop = cur_stop->next;
01714       /* removed the only stop? */
01715       if (*primary_stop == NULL) {
01716         st->facilities &= (is_truck ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP);
01717       }
01718     } else {
01719       /* tell the predecessor in the list to skip this stop */
01720       RoadStop *pred = *primary_stop;
01721       while (pred->next != cur_stop) pred = pred->next;
01722       pred->next = cur_stop->next;
01723     }
01724 
01725     if (IsDriveThroughStopTile(tile)) {
01726       /* Clears the tile for us */
01727       cur_stop->ClearDriveThrough();
01728     } else {
01729       DoClearSquare(tile);
01730     }
01731 
01732     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_ROADVEHS);
01733     delete cur_stop;
01734 
01735     /* Make sure no vehicle is going to the old roadstop */
01736     RoadVehicle *v;
01737     FOR_ALL_ROADVEHICLES(v) {
01738       if (v->First() == v && v->current_order.IsType(OT_GOTO_STATION) &&
01739           v->dest_tile == tile) {
01740         v->dest_tile = v->GetOrderStationLocation(st->index);
01741       }
01742     }
01743 
01744     st->rect.AfterRemoveTile(st, tile);
01745 
01746     st->UpdateVirtCoord();
01747     st->RecomputeIndustriesNear();
01748     DeleteStationIfEmpty(st);
01749 
01750     /* Update the tile area of the truck/bus stop */
01751     if (is_truck) {
01752       st->truck_station.Clear();
01753       for (const RoadStop *rs = st->truck_stops; rs != NULL; rs = rs->next) st->truck_station.Add(rs->xy);
01754     } else {
01755       st->bus_station.Clear();
01756       for (const RoadStop *rs = st->bus_stops; rs != NULL; rs = rs->next) st->bus_station.Add(rs->xy);
01757     }
01758   }
01759 
01760   return CommandCost(EXPENSES_CONSTRUCTION, _price[is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS]);
01761 }
01762 
01771 CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01772 {
01773   /* Make sure the specified tile is a road stop of the correct type */
01774   if (!IsTileType(tile, MP_STATION) || !IsRoadStop(tile) || (uint32)GetRoadStopType(tile) != GB(p2, 0, 1)) return CMD_ERROR;
01775 
01776   /* Save the stop info before it is removed */
01777   bool is_drive_through = IsDriveThroughStopTile(tile);
01778   RoadTypes rts = GetRoadTypes(tile);
01779   RoadBits road_bits = IsDriveThroughStopTile(tile) ?
01780       ((GetRoadStopDir(tile) == DIAGDIR_NE) ? ROAD_X : ROAD_Y) :
01781       DiagDirToRoadBits(GetRoadStopDir(tile));
01782 
01783   Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
01784   Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
01785   CommandCost ret = RemoveRoadStop(tile, flags);
01786 
01787   /* If the stop was a drive-through stop replace the road */
01788   if ((flags & DC_EXEC) && ret.Succeeded() && is_drive_through) {
01789     /* Rebuild the drive throuhg road stop. As a road stop can only be
01790      * removed by the owner of the roadstop, _current_company is the
01791      * owner of the road stop. */
01792     MakeRoadNormal(tile, road_bits, rts, ClosestTownFromTile(tile, UINT_MAX)->index,
01793         road_owner, tram_owner);
01794   }
01795 
01796   return ret;
01797 }
01798 
01806 static uint GetMinimalAirportDistanceToTile(const AirportSpec *as, TileIndex town_tile, TileIndex airport_tile)
01807 {
01808   uint ttx = TileX(town_tile); // X, Y of town
01809   uint tty = TileY(town_tile);
01810 
01811   uint atx = TileX(airport_tile); // X, Y of northern airport corner
01812   uint aty = TileY(airport_tile);
01813 
01814   uint btx = TileX(airport_tile) + as->size_x - 1; // X, Y of southern corner
01815   uint bty = TileY(airport_tile) + as->size_y - 1;
01816 
01817   /* if ttx < atx, dx = atx - ttx
01818    * if atx <= ttx <= btx, dx = 0
01819    * else, dx = ttx - btx (similiar for dy) */
01820   uint dx = ttx < atx ? atx - ttx : (ttx <= btx ? 0 : ttx - btx);
01821   uint dy = tty < aty ? aty - tty : (tty <= bty ? 0 : tty - bty);
01822 
01823   return dx + dy;
01824 }
01825 
01834 uint8 GetAirportNoiseLevelForTown(const AirportSpec *as, TileIndex town_tile, TileIndex tile)
01835 {
01836   /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
01837    * So no need to go any further*/
01838   if (as->noise_level < 2) return as->noise_level;
01839 
01840   uint distance = GetMinimalAirportDistanceToTile(as, town_tile, tile);
01841 
01842   /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
01843    * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
01844    * Basically, it says that the less tolerant a town is, the bigger the distance before
01845    * an actual decrease can be granted */
01846   uint8 town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
01847 
01848   /* now, we want to have the distance segmented using the distance judged bareable by town
01849    * This will give us the coefficient of reduction the distance provides. */
01850   uint noise_reduction = distance / town_tolerance_distance;
01851 
01852   /* If the noise reduction equals the airport noise itself, don't give it for free.
01853    * Otherwise, simply reduce the airport's level. */
01854   return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
01855 }
01856 
01864 Town *AirportGetNearestTown(const AirportSpec *as, TileIndex airport_tile)
01865 {
01866   Town *t, *nearest = NULL;
01867   uint add = as->size_x + as->size_y - 2; // GetMinimalAirportDistanceToTile can differ from DistanceManhattan by this much
01868   uint mindist = UINT_MAX - add; // prevent overflow
01869   FOR_ALL_TOWNS(t) {
01870     if (DistanceManhattan(t->xy, airport_tile) < mindist + add) { // avoid calling GetMinimalAirportDistanceToTile too often
01871       uint dist = GetMinimalAirportDistanceToTile(as, t->xy, airport_tile);
01872       if (dist < mindist) {
01873         nearest = t;
01874         mindist = dist;
01875       }
01876     }
01877   }
01878 
01879   return nearest;
01880 }
01881 
01882 
01884 void UpdateAirportsNoise()
01885 {
01886   Town *t;
01887   const Station *st;
01888 
01889   FOR_ALL_TOWNS(t) t->noise_reached = 0;
01890 
01891   FOR_ALL_STATIONS(st) {
01892     if (st->airport_tile != INVALID_TILE) {
01893       const AirportSpec *as = st->GetAirportSpec();
01894       Town *nearest = AirportGetNearestTown(as, st->airport_tile);
01895       nearest->noise_reached += GetAirportNoiseLevelForTown(as, nearest->xy, st->airport_tile);
01896     }
01897   }
01898 }
01899 
01910 CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01911 {
01912   bool airport_upgrade = true;
01913   StationID station_to_join = GB(p2, 16, 16);
01914   bool reuse = (station_to_join != NEW_STATION);
01915   if (!reuse) station_to_join = INVALID_STATION;
01916   bool distant_join = (station_to_join != INVALID_STATION);
01917 
01918   if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
01919 
01920   if (p1 >= NUM_AIRPORTS) return CMD_ERROR;
01921 
01922   if (!CheckIfAuthorityAllowsNewStation(tile, flags)) {
01923     return CMD_ERROR;
01924   }
01925 
01926   /* Check if a valid, buildable airport was chosen for construction */
01927   const AirportSpec *as = AirportSpec::Get(p1);
01928   if (!as->IsAvailable()) return CMD_ERROR;
01929 
01930   Town *t = ClosestTownFromTile(tile, UINT_MAX);
01931   int w = as->size_x;
01932   int h = as->size_y;
01933 
01934   if (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread) {
01935     _error_message = STR_ERROR_STATION_TOO_SPREAD_OUT;
01936     return CMD_ERROR;
01937   }
01938 
01939   CommandCost cost = CheckFlatLandBelow(tile, w, h, flags, 0, NULL);
01940   if (cost.Failed()) return cost;
01941 
01942   /* Go get the final noise level, that is base noise minus factor from distance to town center */
01943   Town *nearest = AirportGetNearestTown(as, tile);
01944   uint newnoise_level = GetAirportNoiseLevelForTown(as, nearest->xy, tile);
01945 
01946   /* Check if local auth would allow a new airport */
01947   StringID authority_refuse_message = STR_NULL;
01948 
01949   if (_settings_game.economy.station_noise_level) {
01950     /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
01951     if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
01952       authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
01953     }
01954   } else {
01955     uint num = 0;
01956     const Station *st;
01957     FOR_ALL_STATIONS(st) {
01958       if (st->town == t && (st->facilities & FACIL_AIRPORT) && st->airport_type != AT_OILRIG) num++;
01959     }
01960     if (num >= 2) {
01961       authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
01962     }
01963   }
01964 
01965   if (authority_refuse_message != STR_NULL) {
01966     SetDParam(0, t->index);
01967     return_cmd_error(authority_refuse_message);
01968   }
01969 
01970   Station *st = NULL;
01971   CommandCost ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p2, 0), TileArea(tile, w, h), &st);
01972   if (ret.Failed()) return ret;
01973 
01974   /* Distant join */
01975   if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join);
01976 
01977   /* Find a deleted station close to us */
01978   if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
01979 
01980   if (st != NULL) {
01981     if (st->owner != _current_company) {
01982       return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
01983     }
01984 
01985     if (!st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TEST)) return CMD_ERROR;
01986 
01987     if (st->airport_tile != INVALID_TILE) {
01988       return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
01989     }
01990   } else {
01991     airport_upgrade = false;
01992 
01993     /* allocate and initialize new station */
01994     if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
01995 
01996     if (flags & DC_EXEC) {
01997       st = new Station(tile);
01998 
01999       st->town = t;
02000       st->string_id = GenerateStationName(st, tile, !(GetAirport(p1)->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_HELIPORT : STATIONNAMING_AIRPORT);
02001 
02002       if (Company::IsValidID(_current_company)) {
02003         SetBit(st->town->have_ratings, _current_company);
02004       }
02005     }
02006   }
02007 
02008   cost.AddCost(_price[PR_BUILD_STATION_AIRPORT] * w * h);
02009 
02010   if (flags & DC_EXEC) {
02011     /* Always add the noise, so there will be no need to recalculate when option toggles */
02012     nearest->noise_reached += newnoise_level;
02013 
02014     st->airport_tile = tile;
02015     st->AddFacility(FACIL_AIRPORT, tile);
02016     st->airport_type = (byte)p1;
02017     st->airport_flags = 0;
02018 
02019     st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
02020 
02021     /* if airport was demolished while planes were en-route to it, the
02022      * positions can no longer be the same (v->u.air.pos), since different
02023      * airports have different indexes. So update all planes en-route to this
02024      * airport. Only update if
02025      * 1. airport is upgraded
02026      * 2. airport is added to existing station (unfortunately unavoideable)
02027      */
02028     if (airport_upgrade) UpdateAirplanesOnNewStation(st);
02029 
02030     const AirportTileTable *it = as->table[0];
02031     do {
02032       TileIndex cur_tile = tile + ToTileIndexDiff(it->ti);
02033       MakeAirport(cur_tile, st->owner, st->index, it->gfx);
02034     } while ((++it)->ti.x != -0x80);
02035 
02036     st->UpdateVirtCoord();
02037     UpdateStationAcceptance(st, false);
02038     st->RecomputeIndustriesNear();
02039     InvalidateWindowData(WC_SELECT_STATION, 0, 0);
02040     InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
02041     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_PLANES);
02042 
02043     if (_settings_game.economy.station_noise_level) {
02044       SetWindowDirty(WC_TOWN_VIEW, st->town->index);
02045     }
02046   }
02047 
02048   return cost;
02049 }
02050 
02057 static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags)
02058 {
02059   Station *st = Station::GetByTile(tile);
02060 
02061   if (_current_company != OWNER_WATER && !CheckOwnership(st->owner)) {
02062     return CMD_ERROR;
02063   }
02064 
02065   tile = st->airport_tile;
02066 
02067   const AirportSpec *as = st->GetAirportSpec();
02068   int w = as->size_x;
02069   int h = as->size_y;
02070 
02071   CommandCost cost(EXPENSES_CONSTRUCTION);
02072 
02073   const Aircraft *a;
02074   FOR_ALL_AIRCRAFT(a) {
02075     if (!a->IsNormalAircraft()) continue;
02076     if (a->targetairport == st->index && a->state != FLYING) return CMD_ERROR;
02077   }
02078 
02079   TILE_LOOP(tile_cur, w, h, tile) {
02080     if (!EnsureNoVehicleOnGround(tile_cur)) return CMD_ERROR;
02081 
02082     if (!st->TileBelongsToAirport(tile_cur)) continue;
02083 
02084     cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]);
02085 
02086     if (flags & DC_EXEC) {
02087       DeleteAnimatedTile(tile_cur);
02088       DoClearSquare(tile_cur);
02089     }
02090   }
02091 
02092   if (flags & DC_EXEC) {
02093     for (uint i = 0; i < as->nof_depots; ++i) {
02094       DeleteWindowById(
02095         WC_VEHICLE_DEPOT, st->GetHangarTile(i)
02096       );
02097     }
02098 
02099     /* Go get the final noise level, that is base noise minus factor from distance to town center.
02100      * And as for construction, always remove it, even if the setting is not set, in order to avoid the
02101      * need of recalculation */
02102     Town *nearest = AirportGetNearestTown(as, tile);
02103     nearest->noise_reached -= GetAirportNoiseLevelForTown(as, nearest->xy, tile);
02104 
02105     st->rect.AfterRemoveRect(st, tile, w, h);
02106 
02107     st->airport_tile = INVALID_TILE;
02108     st->facilities &= ~FACIL_AIRPORT;
02109 
02110     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_PLANES);
02111 
02112     if (_settings_game.economy.station_noise_level) {
02113       SetWindowDirty(WC_TOWN_VIEW, st->town->index);
02114     }
02115 
02116     st->UpdateVirtCoord();
02117     st->RecomputeIndustriesNear();
02118     DeleteStationIfEmpty(st);
02119   }
02120 
02121   return cost;
02122 }
02123 
02130 bool HasStationInUse(StationID station, CompanyID company)
02131 {
02132   const Vehicle *v;
02133   FOR_ALL_VEHICLES(v) {
02134     if (company == INVALID_COMPANY || v->owner == company) {
02135       const Order *order;
02136       FOR_VEHICLE_ORDERS(v, order) {
02137         if ((order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT)) && order->GetDestination() == station) {
02138           return true;
02139         }
02140       }
02141     }
02142   }
02143   return false;
02144 }
02145 
02146 static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
02147   {-1,  0},
02148   { 0,  0},
02149   { 0,  0},
02150   { 0, -1}
02151 };
02152 static const byte _dock_w_chk[4] = { 2, 1, 2, 1 };
02153 static const byte _dock_h_chk[4] = { 1, 2, 1, 2 };
02154 
02163 CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02164 {
02165   StationID station_to_join = GB(p2, 16, 16);
02166   bool reuse = (station_to_join != NEW_STATION);
02167   if (!reuse) station_to_join = INVALID_STATION;
02168   bool distant_join = (station_to_join != INVALID_STATION);
02169 
02170   if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
02171 
02172   DiagDirection direction = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
02173   if (direction == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
02174   direction = ReverseDiagDir(direction);
02175 
02176   /* Docks cannot be placed on rapids */
02177   if (IsWaterTile(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
02178 
02179   if (!CheckIfAuthorityAllowsNewStation(tile, flags)) return CMD_ERROR;
02180 
02181   if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
02182 
02183   if (DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR).Failed()) return CMD_ERROR;
02184 
02185   TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
02186 
02187   if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
02188     return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
02189   }
02190 
02191   if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
02192 
02193   /* Get the water class of the water tile before it is cleared.*/
02194   WaterClass wc = GetWaterClass(tile_cur);
02195 
02196   if (DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR).Failed()) return CMD_ERROR;
02197 
02198   tile_cur += TileOffsByDiagDir(direction);
02199   if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
02200     return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
02201   }
02202 
02203   /* middle */
02204   Station *st = NULL;
02205   CommandCost ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p1, 0),
02206       TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02207           _dock_w_chk[direction], _dock_h_chk[direction]), &st);
02208   if (ret.Failed()) return ret;
02209 
02210   /* Distant join */
02211   if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join);
02212 
02213   /* Find a deleted station close to us */
02214   if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
02215 
02216   if (st != NULL) {
02217     if (st->owner != _current_company) {
02218       return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
02219     }
02220 
02221     if (!st->rect.BeforeAddRect(
02222         tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02223         _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TEST)) return CMD_ERROR;
02224 
02225     if (st->dock_tile != INVALID_TILE) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_DOCK);
02226   } else {
02227     /* allocate and initialize new station */
02228     if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
02229 
02230     if (flags & DC_EXEC) {
02231       st = new Station(tile);
02232 
02233       st->town = ClosestTownFromTile(tile, UINT_MAX);
02234       st->string_id = GenerateStationName(st, tile, STATIONNAMING_DOCK);
02235 
02236       if (Company::IsValidID(_current_company)) {
02237         SetBit(st->town->have_ratings, _current_company);
02238       }
02239     }
02240   }
02241 
02242   if (flags & DC_EXEC) {
02243     st->dock_tile = tile;
02244     st->AddFacility(FACIL_DOCK, tile);
02245 
02246     st->rect.BeforeAddRect(
02247         tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
02248         _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TRY);
02249 
02250     MakeDock(tile, st->owner, st->index, direction, wc);
02251 
02252     st->UpdateVirtCoord();
02253     UpdateStationAcceptance(st, false);
02254     st->RecomputeIndustriesNear();
02255     InvalidateWindowData(WC_SELECT_STATION, 0, 0);
02256     InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
02257     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_SHIPS);
02258   }
02259 
02260   return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
02261 }
02262 
02269 static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags)
02270 {
02271   Station *st = Station::GetByTile(tile);
02272   if (!CheckOwnership(st->owner)) return CMD_ERROR;
02273 
02274   TileIndex tile1 = st->dock_tile;
02275   TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
02276 
02277   if (!EnsureNoVehicleOnGround(tile1)) return CMD_ERROR;
02278   if (!EnsureNoVehicleOnGround(tile2)) return CMD_ERROR;
02279 
02280   if (flags & DC_EXEC) {
02281     DoClearSquare(tile1);
02282     MakeWaterKeepingClass(tile2, st->owner);
02283 
02284     st->rect.AfterRemoveTile(st, tile1);
02285     st->rect.AfterRemoveTile(st, tile2);
02286 
02287     MarkTileDirtyByTile(tile2);
02288 
02289     st->dock_tile = INVALID_TILE;
02290     st->facilities &= ~FACIL_DOCK;
02291 
02292     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_SHIPS);
02293     st->UpdateVirtCoord();
02294     st->RecomputeIndustriesNear();
02295     DeleteStationIfEmpty(st);
02296   }
02297 
02298   return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]);
02299 }
02300 
02301 #include "table/station_land.h"
02302 
02303 const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx)
02304 {
02305   return &_station_display_datas[st][gfx];
02306 }
02307 
02308 static void DrawTile_Station(TileInfo *ti)
02309 {
02310   const DrawTileSprites *t = NULL;
02311   RoadTypes roadtypes;
02312   int32 total_offset;
02313   int32 custom_ground_offset;
02314   const RailtypeInfo *rti = NULL;
02315   uint32 relocation = 0;
02316   const BaseStation *st = NULL;
02317   const StationSpec *statspec = NULL;
02318 
02319   if (HasStationRail(ti->tile)) {
02320     rti = GetRailTypeInfo(GetRailType(ti->tile));
02321     roadtypes = ROADTYPES_NONE;
02322     total_offset = rti->total_offset;
02323     custom_ground_offset = rti->custom_ground_offset;
02324 
02325     if (IsCustomStationSpecIndex(ti->tile)) {
02326       /* look for customization */
02327       st = BaseStation::GetByTile(ti->tile);
02328       statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
02329 
02330       if (statspec != NULL) {
02331         uint tile = GetStationGfx(ti->tile);
02332 
02333         relocation = GetCustomStationRelocation(statspec, st, ti->tile);
02334 
02335         if (HasBit(statspec->callback_mask, CBM_STATION_SPRITE_LAYOUT)) {
02336           uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile);
02337           if (callback != CALLBACK_FAILED) tile = (callback & ~1) + GetRailStationAxis(ti->tile);
02338         }
02339 
02340         /* Ensure the chosen tile layout is valid for this custom station */
02341         if (statspec->renderdata != NULL) {
02342           t = &statspec->renderdata[tile < statspec->tiles ? tile : (uint)GetRailStationAxis(ti->tile)];
02343         }
02344       }
02345     }
02346   } else {
02347     roadtypes = IsRoadStop(ti->tile) ? GetRoadTypes(ti->tile) : ROADTYPES_NONE;
02348     total_offset = 0;
02349     custom_ground_offset = 0;
02350   }
02351 
02352   if (IsAirport(ti->tile)) {
02353     switch (GetStationGfx(ti->tile)) {
02354       case APT_RADAR_GRASS_FENCE_SW:
02355         t = &_station_display_datas_airport_radar_grass_fence_sw[GetStationAnimationFrame(ti->tile)];
02356         break;
02357       case APT_GRASS_FENCE_NE_FLAG:
02358         t = &_station_display_datas_airport_flag_grass_fence_ne[GetStationAnimationFrame(ti->tile)];
02359         break;
02360       case APT_RADAR_FENCE_SW:
02361         t = &_station_display_datas_airport_radar_fence_sw[GetStationAnimationFrame(ti->tile)];
02362         break;
02363       case APT_RADAR_FENCE_NE:
02364         t = &_station_display_datas_airport_radar_fence_ne[GetStationAnimationFrame(ti->tile)];
02365         break;
02366       case APT_GRASS_FENCE_NE_FLAG_2:
02367         t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetStationAnimationFrame(ti->tile)];
02368         break;
02369     }
02370   }
02371 
02372   Owner owner = GetTileOwner(ti->tile);
02373 
02374   PaletteID palette;
02375   if (Company::IsValidID(owner)) {
02376     palette = COMPANY_SPRITE_COLOUR(owner);
02377   } else {
02378     /* Some stations are not owner by a company, namely oil rigs */
02379     palette = PALETTE_TO_GREY;
02380   }
02381 
02382   if (t == NULL || t->seq == NULL) t = &_station_display_datas[GetStationType(ti->tile)][GetStationGfx(ti->tile)];
02383 
02384   /* don't show foundation for docks */
02385   if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
02386     if (statspec != NULL && HasBit(statspec->flags, SSF_CUSTOM_FOUNDATIONS)) {
02387       /* Station has custom foundations. */
02388       SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile);
02389 
02390       if (HasBit(statspec->flags, SSF_EXTENDED_FOUNDATIONS)) {
02391         /* Station provides extended foundations. */
02392 
02393         static const uint8 foundation_parts[] = {
02394           0, 0, 0, 0, // Invalid,  Invalid,   Invalid,   SLOPE_SW
02395           0, 1, 2, 3, // Invalid,  SLOPE_EW,  SLOPE_SE,  SLOPE_WSE
02396           0, 4, 5, 6, // Invalid,  SLOPE_NW,  SLOPE_NS,  SLOPE_NWS
02397           7, 8, 9     // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
02398         };
02399 
02400         AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
02401       } else {
02402         /* Draw simple foundations, built up from 8 possible foundation sprites. */
02403 
02404         /* Each set bit represents one of the eight composite sprites to be drawn.
02405          * 'Invalid' entries will not drawn but are included for completeness. */
02406         static const uint8 composite_foundation_parts[] = {
02407           /* Invalid  (00000000), Invalid   (11010001), Invalid   (11100100), SLOPE_SW  (11100000) */
02408              0x00,                0xD1,                 0xE4,                 0xE0,
02409           /* Invalid  (11001010), SLOPE_EW  (11001001), SLOPE_SE  (11000100), SLOPE_WSE (11000000) */
02410              0xCA,                0xC9,                 0xC4,                 0xC0,
02411           /* Invalid  (11010010), SLOPE_NW  (10010001), SLOPE_NS  (11100100), SLOPE_NWS (10100000) */
02412              0xD2,                0x91,                 0xE4,                 0xA0,
02413           /* SLOPE_NE (01001010), SLOPE_ENW (00001001), SLOPE_SEN (01000100) */
02414              0x4A,                0x09,                 0x44
02415         };
02416 
02417         uint8 parts = composite_foundation_parts[ti->tileh];
02418 
02419         /* If foundations continue beyond the tile's upper sides then
02420          * mask out the last two pieces. */
02421         uint z;
02422         Slope slope = GetFoundationSlope(ti->tile, &z);
02423         if (!HasFoundationNW(ti->tile, slope, z)) ClrBit(parts, 6);
02424         if (!HasFoundationNE(ti->tile, slope, z)) ClrBit(parts, 7);
02425 
02426         StartSpriteCombine();
02427         for (int i = 0; i < 8; i++) {
02428           if (HasBit(parts, i)) {
02429             AddSortableSpriteToDraw(image + i, PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
02430           }
02431         }
02432         EndSpriteCombine();
02433       }
02434 
02435       OffsetGroundSprite(31, 1);
02436       ti->z += ApplyFoundationToSlope(FOUNDATION_LEVELED, &ti->tileh);
02437     } else {
02438       DrawFoundation(ti, FOUNDATION_LEVELED);
02439     }
02440   }
02441 
02442   if (IsBuoy(ti->tile) || IsDock(ti->tile) || (IsOilRig(ti->tile) && GetWaterClass(ti->tile) != WATER_CLASS_INVALID)) {
02443     if (ti->tileh == SLOPE_FLAT) {
02444       DrawWaterClassGround(ti);
02445     } else {
02446       assert(IsDock(ti->tile));
02447       TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
02448       WaterClass wc = GetWaterClass(water_tile);
02449       if (wc == WATER_CLASS_SEA) {
02450         DrawShoreTile(ti->tileh);
02451       } else {
02452         DrawClearLandTile(ti, 3);
02453       }
02454     }
02455   } else {
02456     SpriteID image = t->ground.sprite;
02457     PaletteID pal  = t->ground.pal;
02458     if (rti != NULL && rti->UsesOverlay() && (image == SPR_RAIL_TRACK_X || image == SPR_RAIL_TRACK_Y)) {
02459       SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
02460       DrawGroundSprite(SPR_FLAT_GRASS_TILE, PAL_NONE);
02461       DrawGroundSprite(ground + (image == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PAL_NONE);
02462 
02463       if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
02464         SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
02465         DrawGroundSprite(overlay + (image == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PALETTE_CRASH);
02466       }
02467     } else {
02468       if (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) {
02469         image += GetCustomStationGroundRelocation(statspec, st, ti->tile);
02470         image += custom_ground_offset;
02471       } else {
02472         image += total_offset;
02473       }
02474       DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
02475 
02476       /* PBS debugging, draw reserved tracks darker */
02477       if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
02478         const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
02479         DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
02480       }
02481     }
02482   }
02483 
02484   if (HasStationRail(ti->tile) && HasCatenaryDrawn(GetRailType(ti->tile)) && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
02485 
02486   if (HasBit(roadtypes, ROADTYPE_TRAM)) {
02487     Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
02488     DrawGroundSprite((HasBit(roadtypes, ROADTYPE_ROAD) ? SPR_TRAMWAY_OVERLAY : SPR_TRAMWAY_TRAM) + (axis ^ 1), PAL_NONE);
02489     DrawTramCatenary(ti, axis == AXIS_X ? ROAD_X : ROAD_Y);
02490   }
02491 
02492   if (IsRailWaypoint(ti->tile)) {
02493     /* Don't offset the waypoint graphics; they're always the same. */
02494     total_offset = 0;
02495   }
02496 
02497   DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
02498 }
02499 
02500 void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
02501 {
02502   int32 total_offset = 0;
02503   PaletteID pal = COMPANY_SPRITE_COLOUR(_local_company);
02504   const DrawTileSprites *t = &_station_display_datas[st][image];
02505   const RailtypeInfo *rti = NULL;
02506 
02507   if (railtype != INVALID_RAILTYPE) {
02508     rti = GetRailTypeInfo(railtype);
02509     total_offset = rti->total_offset;
02510   }
02511 
02512   SpriteID img = t->ground.sprite;
02513   if ((img == SPR_RAIL_TRACK_X || img == SPR_RAIL_TRACK_Y) && rti->UsesOverlay()) {
02514     SpriteID ground = GetCustomRailSprite(rti, INVALID_TILE, RTSG_GROUND);
02515     DrawSprite(SPR_FLAT_GRASS_TILE, PAL_NONE, x, y);
02516     DrawSprite(ground + (img == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PAL_NONE, x, y);
02517   } else {
02518     DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
02519   }
02520 
02521   if (roadtype == ROADTYPE_TRAM) {
02522     DrawSprite(SPR_TRAMWAY_TRAM + (t->ground.sprite == SPR_ROAD_PAVED_STRAIGHT_X ? 1 : 0), PAL_NONE, x, y);
02523   }
02524 
02525   /* Default waypoint has no railtype specific sprites */
02526   DrawRailTileSeqInGUI(x, y, t, st == STATION_WAYPOINT ? 0 : total_offset, 0, pal);
02527 }
02528 
02529 static uint GetSlopeZ_Station(TileIndex tile, uint x, uint y)
02530 {
02531   return GetTileMaxZ(tile);
02532 }
02533 
02534 static Foundation GetFoundation_Station(TileIndex tile, Slope tileh)
02535 {
02536   return FlatteningFoundation(tileh);
02537 }
02538 
02539 static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
02540 {
02541   td->owner[0] = GetTileOwner(tile);
02542   if (IsDriveThroughStopTile(tile)) {
02543     Owner road_owner = INVALID_OWNER;
02544     Owner tram_owner = INVALID_OWNER;
02545     RoadTypes rts = GetRoadTypes(tile);
02546     if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
02547     if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
02548 
02549     /* Is there a mix of owners? */
02550     if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) ||
02551         (road_owner != INVALID_OWNER && road_owner != td->owner[0])) {
02552       uint i = 1;
02553       if (road_owner != INVALID_OWNER) {
02554         td->owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
02555         td->owner[i] = road_owner;
02556         i++;
02557       }
02558       if (tram_owner != INVALID_OWNER) {
02559         td->owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
02560         td->owner[i] = tram_owner;
02561       }
02562     }
02563   }
02564   td->build_date = BaseStation::GetByTile(tile)->build_date;
02565 
02566   if (HasStationTileRail(tile)) {
02567     const StationSpec *spec = GetStationSpec(tile);
02568 
02569     if (spec != NULL) {
02570       td->station_class = GetStationClassName(spec->sclass);
02571       td->station_name  = spec->name;
02572 
02573       if (spec->grffile != NULL) {
02574         const GRFConfig *gc = GetGRFConfig(spec->grffile->grfid);
02575         td->grf = gc->name;
02576       }
02577     }
02578 
02579     const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
02580     td->rail_speed = rti->max_speed;
02581   }
02582 
02583   StringID str;
02584   switch (GetStationType(tile)) {
02585     default: NOT_REACHED();
02586     case STATION_RAIL:     str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
02587     case STATION_AIRPORT:
02588       str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
02589       break;
02590     case STATION_TRUCK:    str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
02591     case STATION_BUS:      str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
02592     case STATION_OILRIG:   str = STR_INDUSTRY_NAME_OIL_RIG; break;
02593     case STATION_DOCK:     str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
02594     case STATION_BUOY:     str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
02595     case STATION_WAYPOINT: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
02596   }
02597   td->str = str;
02598 }
02599 
02600 
02601 static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
02602 {
02603   TrackBits trackbits = TRACK_BIT_NONE;
02604 
02605   switch (mode) {
02606     case TRANSPORT_RAIL:
02607       if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
02608         trackbits = TrackToTrackBits(GetRailStationTrack(tile));
02609       }
02610       break;
02611 
02612     case TRANSPORT_WATER:
02613       /* buoy is coded as a station, it is always on open water */
02614       if (IsBuoy(tile)) {
02615         trackbits = TRACK_BIT_ALL;
02616         /* remove tracks that connect NE map edge */
02617         if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
02618         /* remove tracks that connect NW map edge */
02619         if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
02620       }
02621       break;
02622 
02623     case TRANSPORT_ROAD:
02624       if ((GetRoadTypes(tile) & sub_mode) != 0 && IsRoadStop(tile)) {
02625         DiagDirection dir = GetRoadStopDir(tile);
02626         Axis axis = DiagDirToAxis(dir);
02627 
02628         if (side != INVALID_DIAGDIR) {
02629           if (axis != DiagDirToAxis(side) || (IsStandardRoadStopTile(tile) && dir != side)) break;
02630         }
02631 
02632         trackbits = AxisToTrackBits(axis);
02633       }
02634       break;
02635 
02636     default:
02637       break;
02638   }
02639 
02640   return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), TRACKDIR_BIT_NONE);
02641 }
02642 
02643 
02644 static void TileLoop_Station(TileIndex tile)
02645 {
02646   /* FIXME -- GetTileTrackStatus_Station -> animated stationtiles
02647    * hardcoded.....not good */
02648   switch (GetStationType(tile)) {
02649     case STATION_AIRPORT:
02650       if (AirportTileSpec::Get(GetStationGfx(tile))->animation_info != 0xFFFF) {
02651         AddAnimatedTile(tile);
02652       }
02653       break;
02654 
02655     case STATION_DOCK:
02656       if (GetTileSlope(tile, NULL) != SLOPE_FLAT) break; // only handle water part
02657     /* FALL THROUGH */
02658     case STATION_OILRIG: //(station part)
02659     case STATION_BUOY:
02660       TileLoop_Water(tile);
02661       break;
02662 
02663     default: break;
02664   }
02665 }
02666 
02667 
02668 static void AnimateTile_Station(TileIndex tile)
02669 {
02670   if (HasStationRail(tile)) {
02671     AnimateStationTile(tile);
02672     return;
02673   }
02674 
02675   if (IsAirport(tile)) {
02676     const AirportTileSpec *ats = AirportTileSpec::Get(GetStationGfx(tile));
02677     uint16 mask = (1 << ats->animation_speed) - 1;
02678     if (ats->animation_info != 0xFFFF && (_tick_counter & mask) == 0) {
02679       uint8 next_frame = GetStationAnimationFrame(tile) + 1;
02680       if (next_frame >= GB(ats->animation_info, 0, 8)) next_frame = 0;
02681       SetStationAnimationFrame(tile, next_frame);
02682       MarkTileDirtyByTile(tile);
02683     }
02684   }
02685 }
02686 
02687 
02688 static bool ClickTile_Station(TileIndex tile)
02689 {
02690   const BaseStation *st = BaseStation::GetByTile(tile);
02691 
02692   if (st->facilities & FACIL_WAYPOINT) {
02693     ShowWaypointWindow(Waypoint::From(st));
02694   } else if (IsHangar(tile)) {
02695     ShowDepotWindow(tile, VEH_AIRCRAFT);
02696   } else {
02697     ShowStationViewWindow(st->index);
02698   }
02699   return true;
02700 }
02701 
02702 static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
02703 {
02704   if (v->type == VEH_TRAIN) {
02705     StationID station_id = GetStationIndex(tile);
02706     if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
02707     if (!IsRailStation(tile) || !Train::From(v)->IsFrontEngine()) return VETSB_CONTINUE;
02708 
02709     int station_ahead;
02710     int station_length;
02711     int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
02712 
02713     /* Stop whenever that amount of station ahead + the distance from the
02714      * begin of the platform to the stop location is longer than the length
02715      * of the platform. Station ahead 'includes' the current tile where the
02716      * vehicle is on, so we need to substract that. */
02717     if (!IsInsideBS(stop + station_ahead, station_length, TILE_SIZE)) return VETSB_CONTINUE;
02718 
02719     DiagDirection dir = DirToDiagDir(v->direction);
02720 
02721     x &= 0xF;
02722     y &= 0xF;
02723 
02724     if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
02725     if (y == TILE_SIZE / 2) {
02726       if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
02727       stop &= TILE_SIZE - 1;
02728 
02729       if (x == stop) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station
02730       if (x < stop) {
02731         uint16 spd;
02732 
02733         v->vehstatus |= VS_TRAIN_SLOWING;
02734         spd = max(0, (stop - x) * 20 - 15);
02735         if (spd < v->cur_speed) v->cur_speed = spd;
02736       }
02737     }
02738   } else if (v->type == VEH_ROAD) {
02739     RoadVehicle *rv = RoadVehicle::From(v);
02740     if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
02741       if (IsRoadStop(tile) && rv->IsRoadVehFront()) {
02742         /* Attempt to allocate a parking bay in a road stop */
02743         return RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv) ? VETSB_CONTINUE : VETSB_CANNOT_ENTER;
02744       }
02745     }
02746   }
02747 
02748   return VETSB_CONTINUE;
02749 }
02750 
02757 static bool StationHandleBigTick(BaseStation *st)
02758 {
02759   if (!st->IsInUse() && ++st->delete_ctr >= 8) {
02760     delete st;
02761     return false;
02762   }
02763 
02764   if ((st->facilities & FACIL_WAYPOINT) == 0) UpdateStationAcceptance(Station::From(st), true);
02765 
02766   return true;
02767 }
02768 
02769 static inline void byte_inc_sat(byte *p)
02770 {
02771   byte b = *p + 1;
02772   if (b != 0) *p = b;
02773 }
02774 
02775 static void UpdateStationRating(Station *st)
02776 {
02777   bool waiting_changed = false;
02778 
02779   byte_inc_sat(&st->time_since_load);
02780   byte_inc_sat(&st->time_since_unload);
02781 
02782   const CargoSpec *cs;
02783   FOR_ALL_CARGOSPECS(cs) {
02784     GoodsEntry *ge = &st->goods[cs->Index()];
02785     /* Slowly increase the rating back to his original level in the case we
02786      *  didn't deliver cargo yet to this station. This happens when a bribe
02787      *  failed while you didn't moved that cargo yet to a station. */
02788     if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP) && ge->rating < INITIAL_STATION_RATING) {
02789       ge->rating++;
02790     }
02791 
02792     /* Only change the rating if we are moving this cargo */
02793     if (HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) {
02794       byte_inc_sat(&ge->days_since_pickup);
02795 
02796       bool skip = false;
02797       int rating = 0;
02798       uint waiting = ge->cargo.Count();
02799 
02800       if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) {
02801         /* Perform custom station rating. If it succeeds the speed, days in transit and
02802          * waiting cargo ratings must not be executed. */
02803 
02804         /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
02805         uint last_speed = ge->last_speed;
02806         if (last_speed == 0) last_speed = 0xFF;
02807 
02808         uint32 var18 = min(ge->days_since_pickup, 0xFF) | (min(waiting, 0xFFFF) << 8) | (min(last_speed, 0xFF) << 24);
02809         /* Convert to the 'old' vehicle types */
02810         uint32 var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
02811         uint16 callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
02812         if (callback != CALLBACK_FAILED) {
02813           skip = true;
02814           rating = GB(callback, 0, 14);
02815 
02816           /* Simulate a 15 bit signed value */
02817           if (HasBit(callback, 14)) rating -= 0x4000;
02818         }
02819       }
02820 
02821       if (!skip) {
02822         int b = ge->last_speed - 85;
02823         if (b >= 0) rating += b >> 2;
02824 
02825         byte days = ge->days_since_pickup;
02826         if (st->last_vehicle_type == VEH_SHIP) days >>= 2;
02827         (days > 21) ||
02828         (rating += 25, days > 12) ||
02829         (rating += 25, days > 6) ||
02830         (rating += 45, days > 3) ||
02831         (rating += 35, true);
02832 
02833         (rating -= 90, waiting > 1500) ||
02834         (rating += 55, waiting > 1000) ||
02835         (rating += 35, waiting > 600) ||
02836         (rating += 10, waiting > 300) ||
02837         (rating += 20, waiting > 100) ||
02838         (rating += 10, true);
02839       }
02840 
02841       if (Company::IsValidID(st->owner) && HasBit(st->town->statues, st->owner)) rating += 26;
02842 
02843       byte age = ge->last_age;
02844       (age >= 3) ||
02845       (rating += 10, age >= 2) ||
02846       (rating += 10, age >= 1) ||
02847       (rating += 13, true);
02848 
02849       {
02850         int or_ = ge->rating; // old rating
02851 
02852         /* only modify rating in steps of -2, -1, 0, 1 or 2 */
02853         ge->rating = rating = or_ + Clamp(Clamp(rating, 0, 255) - or_, -2, 2);
02854 
02855         /* if rating is <= 64 and more than 200 items waiting,
02856          * remove some random amount of goods from the station */
02857         if (rating <= 64 && waiting >= 200) {
02858           int dec = Random() & 0x1F;
02859           if (waiting < 400) dec &= 7;
02860           waiting -= dec + 1;
02861           waiting_changed = true;
02862         }
02863 
02864         /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
02865         if (rating <= 127 && waiting != 0) {
02866           uint32 r = Random();
02867           if (rating <= (int)GB(r, 0, 7)) {
02868             /* Need to have int, otherwise it will just overflow etc. */
02869             waiting = max((int)waiting - (int)GB(r, 8, 2) - 1, 0);
02870             waiting_changed = true;
02871           }
02872         }
02873 
02874         /* At some point we really must cap the cargo. Previously this
02875          * was a strict 4095, but now we'll have a less strict, but
02876          * increasingly agressive truncation of the amount of cargo. */
02877         static const uint WAITING_CARGO_THRESHOLD  = 1 << 12;
02878         static const uint WAITING_CARGO_CUT_FACTOR = 1 <<  6;
02879         static const uint MAX_WAITING_CARGO        = 1 << 15;
02880 
02881         if (waiting > WAITING_CARGO_THRESHOLD) {
02882           uint difference = waiting - WAITING_CARGO_THRESHOLD;
02883           waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
02884 
02885           waiting = min(waiting, MAX_WAITING_CARGO);
02886           waiting_changed = true;
02887         }
02888 
02889         if (waiting_changed) ge->cargo.Truncate(waiting);
02890       }
02891     }
02892   }
02893 
02894   StationID index = st->index;
02895   if (waiting_changed) {
02896     SetWindowDirty(WC_STATION_VIEW, index); // update whole window
02897   } else {
02898     SetWindowWidgetDirty(WC_STATION_VIEW, index, SVW_RATINGLIST); // update only ratings list
02899   }
02900 }
02901 
02902 /* called for every station each tick */
02903 static void StationHandleSmallTick(BaseStation *st)
02904 {
02905   if ((st->facilities & FACIL_WAYPOINT) != 0 || !st->IsInUse()) return;
02906 
02907   byte b = st->delete_ctr + 1;
02908   if (b >= 185) b = 0;
02909   st->delete_ctr = b;
02910 
02911   if (b == 0) UpdateStationRating(Station::From(st));
02912 }
02913 
02914 void OnTick_Station()
02915 {
02916   if (_game_mode == GM_EDITOR) return;
02917 
02918   BaseStation *st;
02919   FOR_ALL_BASE_STATIONS(st) {
02920     StationHandleSmallTick(st);
02921 
02922     /* Run 250 tick interval trigger for station animation.
02923      * Station index is included so that triggers are not all done
02924      * at the same time. */
02925     if ((_tick_counter + st->index) % 250 == 0) {
02926       /* Stop processing this station if it was deleted */
02927       if (!StationHandleBigTick(st)) continue;
02928       StationAnimationTrigger(st, st->xy, STAT_ANIM_250_TICKS);
02929     }
02930   }
02931 }
02932 
02933 void StationMonthlyLoop()
02934 {
02935   /* not used */
02936 }
02937 
02938 
02939 void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
02940 {
02941   Station *st;
02942 
02943   FOR_ALL_STATIONS(st) {
02944     if (st->owner == owner &&
02945         DistanceManhattan(tile, st->xy) <= radius) {
02946       for (CargoID i = 0; i < NUM_CARGO; i++) {
02947         GoodsEntry *ge = &st->goods[i];
02948 
02949         if (ge->acceptance_pickup != 0) {
02950           ge->rating = Clamp(ge->rating + amount, 0, 255);
02951         }
02952       }
02953     }
02954   }
02955 }
02956 
02957 static void UpdateStationWaiting(Station *st, CargoID type, uint amount, SourceType source_type, SourceID source_id)
02958 {
02959   st->goods[type].cargo.Append(new CargoPacket(st->index, st->xy, amount, source_type, source_id));
02960   SetBit(st->goods[type].acceptance_pickup, GoodsEntry::PICKUP);
02961 
02962   StationAnimationTrigger(st, st->xy, STAT_ANIM_NEW_CARGO, type);
02963 
02964   SetWindowDirty(WC_STATION_VIEW, st->index);
02965   st->MarkTilesDirty(true);
02966 }
02967 
02968 static bool IsUniqueStationName(const char *name)
02969 {
02970   const Station *st;
02971 
02972   FOR_ALL_STATIONS(st) {
02973     if (st->name != NULL && strcmp(st->name, name) == 0) return false;
02974   }
02975 
02976   return true;
02977 }
02978 
02987 CommandCost CmdRenameStation(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
02988 {
02989   Station *st = Station::GetIfValid(p1);
02990   if (st == NULL || !CheckOwnership(st->owner)) return CMD_ERROR;
02991 
02992   bool reset = StrEmpty(text);
02993 
02994   if (!reset) {
02995     if (strlen(text) >= MAX_LENGTH_STATION_NAME_BYTES) return CMD_ERROR;
02996     if (!IsUniqueStationName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
02997   }
02998 
02999   if (flags & DC_EXEC) {
03000     free(st->name);
03001     st->name = reset ? NULL : strdup(text);
03002 
03003     st->UpdateVirtCoord();
03004     InvalidateWindowData(WC_STATION_LIST, st->owner, 1);
03005   }
03006 
03007   return CommandCost();
03008 }
03009 
03016 void FindStationsAroundTiles(const TileArea &location, StationList *stations)
03017 {
03018   /* area to search = producer plus station catchment radius */
03019   int max_rad = (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED);
03020 
03021   for (int dy = -max_rad; dy < location.h + max_rad; dy++) {
03022     for (int dx = -max_rad; dx < location.w + max_rad; dx++) {
03023       TileIndex cur_tile = TileAddWrap(location.tile, dx, dy);
03024       if (cur_tile == INVALID_TILE || !IsTileType(cur_tile, MP_STATION)) continue;
03025 
03026       Station *st = Station::GetByTile(cur_tile);
03027       if (st == NULL) continue;
03028 
03029       if (_settings_game.station.modified_catchment) {
03030         int rad = st->GetCatchmentRadius();
03031         if (dx < -rad || dx >= rad + location.w || dy < -rad || dy >= rad + location.h) continue;
03032       }
03033 
03034       /* Insert the station in the set. This will fail if it has
03035        * already been added.
03036        */
03037       stations->Include(st);
03038     }
03039   }
03040 }
03041 
03046 const StationList *StationFinder::GetStations()
03047 {
03048   if (this->tile != INVALID_TILE) {
03049     FindStationsAroundTiles(*this, &this->stations);
03050     this->tile = INVALID_TILE;
03051   }
03052   return &this->stations;
03053 }
03054 
03055 uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList *all_stations)
03056 {
03057   /* Return if nothing to do. Also the rounding below fails for 0. */
03058   if (amount == 0) return 0;
03059 
03060   Station *st1 = NULL;   // Station with best rating
03061   Station *st2 = NULL;   // Second best station
03062   uint best_rating1 = 0; // rating of st1
03063   uint best_rating2 = 0; // rating of st2
03064 
03065   for (Station * const *st_iter = all_stations->Begin(); st_iter != all_stations->End(); ++st_iter) {
03066     Station *st = *st_iter;
03067 
03068     /* Is the station reserved exclusively for somebody else? */
03069     if (st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) continue;
03070 
03071     if (st->goods[type].rating == 0) continue; // Lowest possible rating, better not to give cargo anymore
03072 
03073     if (_settings_game.order.selectgoods && st->goods[type].last_speed == 0) continue; // Selectively servicing stations, and not this one
03074 
03075     if (IsCargoInClass(type, CC_PASSENGERS)) {
03076       if (st->facilities == FACIL_TRUCK_STOP) continue; // passengers are never served by just a truck stop
03077     } else {
03078       if (st->facilities == FACIL_BUS_STOP) continue; // non-passengers are never served by just a bus stop
03079     }
03080 
03081     /* This station can be used, add it to st1/st2 */
03082     if (st1 == NULL || st->goods[type].rating >= best_rating1) {
03083       st2 = st1; best_rating2 = best_rating1; st1 = st; best_rating1 = st->goods[type].rating;
03084     } else if (st2 == NULL || st->goods[type].rating >= best_rating2) {
03085       st2 = st; best_rating2 = st->goods[type].rating;
03086     }
03087   }
03088 
03089   /* no stations around at all? */
03090   if (st1 == NULL) return 0;
03091 
03092   if (st2 == NULL) {
03093     /* only one station around */
03094     uint moved = amount * best_rating1 / 256 + 1;
03095     UpdateStationWaiting(st1, type, moved, source_type, source_id);
03096     return moved;
03097   }
03098 
03099   /* several stations around, the best two (highest rating) are in st1 and st2 */
03100   assert(st1 != NULL);
03101   assert(st2 != NULL);
03102   assert(best_rating1 != 0 || best_rating2 != 0);
03103 
03104   /* the 2nd highest one gets a penalty */
03105   best_rating2 >>= 1;
03106 
03107   /* amount given to station 1 */
03108   uint t = (best_rating1 * (amount + 1)) / (best_rating1 + best_rating2);
03109 
03110   uint moved = 0;
03111   if (t != 0) {
03112     moved = t * best_rating1 / 256 + 1;
03113     amount -= t;
03114     UpdateStationWaiting(st1, type, moved, source_type, source_id);
03115   }
03116 
03117   if (amount != 0) {
03118     amount = amount * best_rating2 / 256 + 1;
03119     moved += amount;
03120     UpdateStationWaiting(st2, type, amount, source_type, source_id);
03121   }
03122 
03123   return moved;
03124 }
03125 
03126 void BuildOilRig(TileIndex tile)
03127 {
03128   if (!Station::CanAllocateItem()) {
03129     DEBUG(misc, 0, "Can't allocate station for oilrig at 0x%X, reverting to oilrig only", tile);
03130     return;
03131   }
03132 
03133   Station *st = new Station(tile);
03134   st->town = ClosestTownFromTile(tile, UINT_MAX);
03135 
03136   st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
03137 
03138   assert(IsTileType(tile, MP_INDUSTRY));
03139   DeleteAnimatedTile(tile);
03140   MakeOilrig(tile, st->index, GetWaterClass(tile));
03141 
03142   st->owner = OWNER_NONE;
03143   st->airport_type = AT_OILRIG;
03144   st->airport_tile = tile;
03145   st->dock_tile = tile;
03146   st->facilities = FACIL_AIRPORT | FACIL_DOCK;
03147   st->build_date = _date;
03148 
03149   st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
03150 
03151   for (CargoID j = 0; j < NUM_CARGO; j++) {
03152     st->goods[j].acceptance_pickup = 0;
03153     st->goods[j].days_since_pickup = 255;
03154     st->goods[j].rating = INITIAL_STATION_RATING;
03155     st->goods[j].last_speed = 0;
03156     st->goods[j].last_age = 255;
03157   }
03158 
03159   st->UpdateVirtCoord();
03160   UpdateStationAcceptance(st, false);
03161   st->RecomputeIndustriesNear();
03162 }
03163 
03164 void DeleteOilRig(TileIndex tile)
03165 {
03166   Station *st = Station::GetByTile(tile);
03167 
03168   MakeWaterKeepingClass(tile, OWNER_NONE);
03169   MarkTileDirtyByTile(tile);
03170 
03171   st->dock_tile = INVALID_TILE;
03172   st->airport_tile = INVALID_TILE;
03173   st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
03174   st->airport_flags = 0;
03175 
03176   st->rect.AfterRemoveTile(st, tile);
03177 
03178   st->UpdateVirtCoord();
03179   st->RecomputeIndustriesNear();
03180   if (!st->IsInUse()) delete st;
03181 }
03182 
03183 static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
03184 {
03185   if (IsDriveThroughStopTile(tile)) {
03186     for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
03187       /* Update all roadtypes, no matter if they are present */
03188       if (GetRoadOwner(tile, rt) == old_owner) {
03189         SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
03190       }
03191     }
03192   }
03193 
03194   if (!IsTileOwner(tile, old_owner)) return;
03195 
03196   if (new_owner != INVALID_OWNER) {
03197     /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
03198     SetTileOwner(tile, new_owner);
03199     InvalidateWindowClassesData(WC_STATION_LIST, 0);
03200   } else {
03201     if (IsDriveThroughStopTile(tile)) {
03202       /* Remove the drive-through road stop */
03203       DoCommand(tile, 0, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, DC_EXEC | DC_BANKRUPT, CMD_REMOVE_ROAD_STOP);
03204       assert(IsTileType(tile, MP_ROAD));
03205       /* Change owner of tile and all roadtypes */
03206       ChangeTileOwner(tile, old_owner, new_owner);
03207     } else {
03208       DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
03209       /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
03210        * Update owner of buoy if it was not removed (was in orders).
03211        * Do not update when owned by OWNER_WATER (sea and rivers). */
03212       if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
03213     }
03214   }
03215 }
03216 
03225 static bool CanRemoveRoadWithStop(TileIndex tile, DoCommandFlag flags)
03226 {
03227   /* Yeah... water can always remove stops, right? */
03228   if (_current_company == OWNER_WATER) return true;
03229 
03230   Owner road_owner = _current_company;
03231   Owner tram_owner = _current_company;
03232 
03233   RoadTypes rts = GetRoadTypes(tile);
03234   if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
03235   if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
03236 
03237   if ((road_owner != OWNER_TOWN && !CheckOwnership(road_owner)) || !CheckOwnership(tram_owner)) return false;
03238 
03239   return road_owner != OWNER_TOWN || CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_ROAD), OWNER_TOWN, ROADTYPE_ROAD, flags);
03240 }
03241 
03242 CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
03243 {
03244   if (flags & DC_AUTO) {
03245     switch (GetStationType(tile)) {
03246       default: break;
03247       case STATION_RAIL:     return_cmd_error(STR_ERROR_MUST_DEMOLISH_RAILROAD);
03248       case STATION_WAYPOINT: return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
03249       case STATION_AIRPORT:  return_cmd_error(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
03250       case STATION_TRUCK:    return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
03251       case STATION_BUS:      return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
03252       case STATION_BUOY:     return_cmd_error(STR_ERROR_BUOY_IN_THE_WAY);
03253       case STATION_DOCK:     return_cmd_error(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
03254       case STATION_OILRIG:
03255         SetDParam(1, STR_INDUSTRY_NAME_OIL_RIG);
03256         return_cmd_error(STR_ERROR_UNMOVABLE_OBJECT_IN_THE_WAY);
03257     }
03258   }
03259 
03260   switch (GetStationType(tile)) {
03261     case STATION_RAIL:     return RemoveRailStation(tile, flags);
03262     case STATION_WAYPOINT: return RemoveRailWaypoint(tile, flags);
03263     case STATION_AIRPORT:  return RemoveAirport(tile, flags);
03264     case STATION_TRUCK:
03265       if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags))
03266         return_cmd_error(STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
03267       return RemoveRoadStop(tile, flags);
03268     case STATION_BUS:
03269       if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags))
03270         return_cmd_error(STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
03271       return RemoveRoadStop(tile, flags);
03272     case STATION_BUOY:     return RemoveBuoy(tile, flags);
03273     case STATION_DOCK:     return RemoveDock(tile, flags);
03274     default: break;
03275   }
03276 
03277   return CMD_ERROR;
03278 }
03279 
03280 static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
03281 {
03282   if (_settings_game.construction.build_on_slopes && AutoslopeEnabled()) {
03283     /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
03284      *       TTDP does not call it.
03285      */
03286     if (!IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new))) {
03287       switch (GetStationType(tile)) {
03288         case STATION_WAYPOINT:
03289         case STATION_RAIL: {
03290           DiagDirection direction = AxisToDiagDir(GetRailStationAxis(tile));
03291           if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
03292           if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
03293           return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
03294         }
03295 
03296         case STATION_AIRPORT:
03297           return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
03298 
03299         case STATION_TRUCK:
03300         case STATION_BUS: {
03301           DiagDirection direction = GetRoadStopDir(tile);
03302           if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
03303           if (IsDriveThroughStopTile(tile)) {
03304             if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
03305           }
03306           return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
03307         }
03308 
03309         default: break;
03310       }
03311     }
03312   }
03313   return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
03314 }
03315 
03316 
03317 extern const TileTypeProcs _tile_type_station_procs = {
03318   DrawTile_Station,           // draw_tile_proc
03319   GetSlopeZ_Station,          // get_slope_z_proc
03320   ClearTile_Station,          // clear_tile_proc
03321   NULL,                       // add_accepted_cargo_proc
03322   GetTileDesc_Station,        // get_tile_desc_proc
03323   GetTileTrackStatus_Station, // get_tile_track_status_proc
03324   ClickTile_Station,          // click_tile_proc
03325   AnimateTile_Station,        // animate_tile_proc
03326   TileLoop_Station,           // tile_loop_clear
03327   ChangeTileOwner_Station,    // change_tile_owner_clear
03328   NULL,                       // add_produced_cargo_proc
03329   VehicleEnter_Station,       // vehicle_enter_tile_proc
03330   GetFoundation_Station,      // get_foundation_proc
03331   TerraformTile_Station,      // terraform_tile_proc
03332 };

Generated on Sat Apr 17 23:24:53 2010 for OpenTTD by  doxygen 1.6.1