station_cmd.cpp

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

Generated on Wed Dec 30 20:40:06 2009 for OpenTTD by  doxygen 1.5.6