# 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 "aircraft.h"
    00014 #include "bridge_map.h"
    00015 #include "cmd_helper.h"
    00016 #include "viewport_func.h"
    00017 #include "command_func.h"
    00018 #include "town.h"
    00019 #include "news_func.h"
    00020 #include "train.h"
    00021 #include "roadveh.h"
    00022 #include "industry.h"
    00023 #include "newgrf_cargo.h"
    00024 #include "newgrf_debug.h"
    00025 #include "newgrf_station.h"
    00026 #include "pathfinder/yapf/yapf_cache.h"
    00027 #include "road_internal.h" /* For drawing catenary/checking road removal */
    00028 #include "autoslope.h"
    00029 #include "water.h"
    00030 #include "station_gui.h"
    00031 #include "strings_func.h"
    00032 #include "clear_func.h"
    00033 #include "window_func.h"
    00034 #include "date_func.h"
    00035 #include "vehicle_func.h"
    00036 #include "string_func.h"
    00037 #include "animated_tile_func.h"
    00038 #include "elrail_func.h"
    00039 #include "station_base.h"
    00040 #include "roadstop_base.h"
    00041 #include "newgrf_railtype.h"
    00042 #include "waypoint_base.h"
    00043 #include "waypoint_func.h"
    00044 #include "pbs.h"
    00045 #include "debug.h"
    00046 #include "core/random_func.hpp"
    00047 #include "company_base.h"
    00048 #include "moving_average.h"
    00049 #include "table/airporttile_ids.h"
    00050 #include "newgrf_airporttiles.h"
    00051 #include "order_backup.h"
    00052
    00053 #include "table/strings.h"
    00054
    00061 bool IsHangar(TileIndex t)
    00062 {
    00063   assert(IsTileType(t, MP_STATION));
    00064
    00065   /* If the tile isn't an airport there's no chance it's a hangar. */
    00066   if (!IsAirport(t)) return false;
    00067
    00068   const Station *st = Station::GetByTile(t);
    00069   const AirportSpec *as = st->airport.GetSpec();
    00070
    00071   for (uint i = 0; i < as->nof_depots; i++) {
    00072     if (st->airport.GetHangarTile(i) == t) return true;
    00073   }
    00074
    00075   return false;
    00076 }
    00077
    00085 template <class T>
    00086 CommandCost GetStationAround(TileArea ta, StationID closest_station, T **st)
    00087 {
    00088   ta.tile -= TileDiffXY(1, 1);
    00089   ta.w    += 2;
    00090   ta.h    += 2;
    00091
    00092   /* check around to see if there's any stations there */
    00093   TILE_AREA_LOOP(tile_cur, ta) {
    00094     if (IsTileType(tile_cur, MP_STATION)) {
    00095       StationID t = GetStationIndex(tile_cur);
    00096       if (!T::IsValidID(t)) continue;
    00097
    00098       if (closest_station == INVALID_STATION) {
    00099         closest_station = t;
    00100       } else if (closest_station != t) {
    00101         return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
    00102       }
    00103     }
    00104   }
    00105   *st = (closest_station == INVALID_STATION) ? NULL : T::Get(closest_station);
    00106   return CommandCost();
    00107 }
    00108
    00114 typedef bool (*CMSAMatcher)(TileIndex tile);
    00115
    00122 static int CountMapSquareAround(TileIndex tile, CMSAMatcher cmp)
    00123 {
    00124   int num = 0;
    00125
    00126   for (int dx = -3; dx <= 3; dx++) {
    00127     for (int dy = -3; dy <= 3; dy++) {
    00128       TileIndex t = TileAddWrap(tile, dx, dy);
    00129       if (t != INVALID_TILE && cmp(t)) num++;
    00130     }
    00131   }
    00132
    00133   return num;
    00134 }
    00135
    00141 static bool CMSAMine(TileIndex tile)
    00142 {
    00143   /* No industry */
    00144   if (!IsTileType(tile, MP_INDUSTRY)) return false;
    00145
    00146   const Industry *ind = Industry::GetByTile(tile);
    00147
    00148   /* No extractive industry */
    00149   if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_EXTRACTIVE) == 0) return false;
    00150
    00151   for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
    00152     /* The industry extracts something non-liquid, i.e. no oil or plastic, so it is a mine.
    00153      * Also the production of passengers and mail is ignored. */
    00154     if (ind->produced_cargo[i] != CT_INVALID &&
    00155         (CargoSpec::Get(ind->produced_cargo[i])->classes & (CC_LIQUID | CC_PASSENGERS | CC_MAIL)) == 0) {
    00156       return true;
    00157     }
    00158   }
    00159
    00160   return false;
    00161 }
    00162
    00168 static bool CMSAWater(TileIndex tile)
    00169 {
    00170   return IsTileType(tile, MP_WATER) && IsWater(tile);
    00171 }
    00172
    00178 static bool CMSATree(TileIndex tile)
    00179 {
    00180   return IsTileType(tile, MP_TREES);
    00181 }
    00182
    00188 static bool CMSAForest(TileIndex tile)
    00189 {
    00190   /* No industry */
    00191   if (!IsTileType(tile, MP_INDUSTRY)) return false;
    00192
    00193   const Industry *ind = Industry::GetByTile(tile);
    00194
    00195   /* No extractive industry */
    00196   if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_ORGANIC) == 0) return false;
    00197
    00198   for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
    00199     /* The industry produces wood. */
    00200     if (ind->produced_cargo[i] != CT_INVALID && CargoSpec::Get(ind->produced_cargo[i])->label == 'WOOD') return true;
    00201   }
    00202
    00203   return false;
    00204 }
    00205
    00206 #define M(x) ((x) - STR_SV_STNAME)
    00207
    00208 enum StationNaming {
    00209   STATIONNAMING_RAIL,
    00210   STATIONNAMING_ROAD,
    00211   STATIONNAMING_AIRPORT,
    00212   STATIONNAMING_OILRIG,
    00213   STATIONNAMING_DOCK,
    00214   STATIONNAMING_HELIPORT,
    00215 };
    00216
    00218 struct StationNameInformation {
    00219   uint32 free_names;
    00220   bool *indtypes;
    00221 };
    00222
    00231 static bool FindNearIndustryName(TileIndex tile, void *user_data)
    00232 {
    00233   /* All already found industry types */
    00234   StationNameInformation *sni = (StationNameInformation*)user_data;
    00235   if (!IsTileType(tile, MP_INDUSTRY)) return false;
    00236
    00237   /* If the station name is undefined it means that it doesn't name a station */
    00238   IndustryType indtype = GetIndustryType(tile);
    00239   if (GetIndustrySpec(indtype)->station_name == STR_UNDEFINED) return false;
    00240
    00241   /* In all cases if an industry that provides a name is found two of
    00242    * the standard names will be disabled. */
    00243   sni->free_names &= ~(1 << M(STR_SV_STNAME_OILFIELD) | 1 << M(STR_SV_STNAME_MINES));
    00244   return !sni->indtypes[indtype];
    00245 }
    00246
    00247 static StringID GenerateStationName(Station *st, TileIndex tile, StationNaming name_class)
    00248 {
    00249   static const uint32 _gen_station_name_bits[] = {
    00250     0,                                       // STATIONNAMING_RAIL
    00251     0,                                       // STATIONNAMING_ROAD
    00252     1U << M(STR_SV_STNAME_AIRPORT),          // STATIONNAMING_AIRPORT
    00253     1U << M(STR_SV_STNAME_OILFIELD),         // STATIONNAMING_OILRIG
    00254     1U << M(STR_SV_STNAME_DOCKS),            // STATIONNAMING_DOCK
    00255     1U << M(STR_SV_STNAME_HELIPORT),         // STATIONNAMING_HELIPORT
    00256   };
    00257
    00258   const Town *t = st->town;
    00259   uint32 free_names = UINT32_MAX;
    00260
    00261   bool indtypes[NUM_INDUSTRYTYPES];
    00262   memset(indtypes, 0, sizeof(indtypes));
    00263
    00264   const Station *s;
    00265   FOR_ALL_STATIONS(s) {
    00266     if (s != st && s->town == t) {
    00267       if (s->indtype != IT_INVALID) {
    00268         indtypes[s->indtype] = true;
    00269         continue;
    00270       }
    00271       uint str = M(s->string_id);
    00272       if (str <= 0x20) {
    00273         if (str == M(STR_SV_STNAME_FOREST)) {
    00274           str = M(STR_SV_STNAME_WOODS);
    00275         }
    00276         ClrBit(free_names, str);
    00277       }
    00278     }
    00279   }
    00280
    00281   TileIndex indtile = tile;
    00282   StationNameInformation sni = { free_names, indtypes };
    00283   if (CircularTileSearch(&indtile, 7, FindNearIndustryName, &sni)) {
    00284     /* An industry has been found nearby */
    00285     IndustryType indtype = GetIndustryType(indtile);
    00286     const IndustrySpec *indsp = GetIndustrySpec(indtype);
    00287     /* STR_NULL means it only disables oil rig/mines */
    00288     if (indsp->station_name != STR_NULL) {
    00289       st->indtype = indtype;
    00290       return STR_SV_STNAME_FALLBACK;
    00291     }
    00292   }
    00293
    00294   /* Oil rigs/mines name could be marked not free by looking for a near by industry. */
    00295   free_names = sni.free_names;
    00296
    00297   /* check default names */
    00298   uint32 tmp = free_names & _gen_station_name_bits[name_class];
    00299   if (tmp != 0) return STR_SV_STNAME + FindFirstBit(tmp);
    00300
    00301   /* check mine? */
    00302   if (HasBit(free_names, M(STR_SV_STNAME_MINES))) {
    00303     if (CountMapSquareAround(tile, CMSAMine) >= 2) {
    00304       return STR_SV_STNAME_MINES;
    00305     }
    00306   }
    00307
    00308   /* check close enough to town to get central as name? */
    00309   if (DistanceMax(tile, t->xy) < 8) {
    00310     if (HasBit(free_names, M(STR_SV_STNAME))) return STR_SV_STNAME;
    00311
    00312     if (HasBit(free_names, M(STR_SV_STNAME_CENTRAL))) return STR_SV_STNAME_CENTRAL;
    00313   }
    00314
    00315   /* Check lakeside */
    00316   if (HasBit(free_names, M(STR_SV_STNAME_LAKESIDE)) &&
    00317       DistanceFromEdge(tile) < 20 &&
    00318       CountMapSquareAround(tile, CMSAWater) >= 5) {
    00319     return STR_SV_STNAME_LAKESIDE;
    00320   }
    00321
    00322   /* Check woods */
    00323   if (HasBit(free_names, M(STR_SV_STNAME_WOODS)) && (
    00324         CountMapSquareAround(tile, CMSATree) >= 8 ||
    00325         CountMapSquareAround(tile, CMSAForest) >= 2)
    00326       ) {
    00327     return _settings_game.game_creation.landscape == LT_TROPIC ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
    00328   }
    00329
    00330   /* check elevation compared to town */
    00331   uint z = GetTileZ(tile);
    00332   uint z2 = GetTileZ(t->xy);
    00333   if (z < z2) {
    00334     if (HasBit(free_names, M(STR_SV_STNAME_VALLEY))) return STR_SV_STNAME_VALLEY;
    00335   } else if (z > z2) {
    00336     if (HasBit(free_names, M(STR_SV_STNAME_HEIGHTS))) return STR_SV_STNAME_HEIGHTS;
    00337   }
    00338
    00339   /* check direction compared to town */
    00340   static const int8 _direction_and_table[] = {
    00341     ~( (1 << M(STR_SV_STNAME_WEST))  | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
    00342     ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
    00343     ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
    00344     ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) ),
    00345   };
    00346
    00347   free_names &= _direction_and_table[
    00348     (TileX(tile) < TileX(t->xy)) +
    00349     (TileY(tile) < TileY(t->xy)) * 2];
    00350
    00351   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));
    00352   return (tmp == 0) ? STR_SV_STNAME_FALLBACK : (STR_SV_STNAME + FindFirstBit(tmp));
    00353 }
    00354 #undef M
    00355
    00361 static Station *GetClosestDeletedStation(TileIndex tile)
    00362 {
    00363   uint threshold = 8;
    00364   Station *best_station = NULL;
    00365   Station *st;
    00366
    00367   FOR_ALL_STATIONS(st) {
    00368     if (!st->IsInUse() && st->owner == _current_company) {
    00369       uint cur_dist = DistanceManhattan(tile, st->xy);
    00370
    00371       if (cur_dist < threshold) {
    00372         threshold = cur_dist;
    00373         best_station = st;
    00374       }
    00375     }
    00376   }
    00377
    00378   return best_station;
    00379 }
    00380
    00381
    00382 void Station::GetTileArea(TileArea *ta, StationType type) const
    00383 {
    00384   switch (type) {
    00385     case STATION_RAIL:
    00386       *ta = this->train_station;
    00387       return;
    00388
    00389     case STATION_AIRPORT:
    00390       *ta = this->airport;
    00391       return;
    00392
    00393     case STATION_TRUCK:
    00394       *ta = this->truck_station;
    00395       return;
    00396
    00397     case STATION_BUS:
    00398       *ta = this->bus_station;
    00399       return;
    00400
    00401     case STATION_DOCK:
    00402     case STATION_OILRIG:
    00403       ta->tile = this->dock_tile;
    00404       break;
    00405
    00406     default: NOT_REACHED();
    00407   }
    00408
    00409   ta->w = 1;
    00410   ta->h = 1;
    00411 }
    00412
    00416 void Station::UpdateVirtCoord()
    00417 {
    00418   Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE);
    00419
    00420   pt.y -= 32;
    00421   if ((this->facilities & FACIL_AIRPORT) && this->airport.type == AT_OILRIG) pt.y -= 16;
    00422
    00423   SetDParam(0, this->index);
    00424   SetDParam(1, this->facilities);
    00425   this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_STATION);
    00426
    00427   SetWindowDirty(WC_STATION_VIEW, this->index);
    00428 }
    00429
    00431 void UpdateAllStationVirtCoords()
    00432 {
    00433   BaseStation *st;
    00434
    00435   FOR_ALL_BASE_STATIONS(st) {
    00436     st->UpdateVirtCoord();
    00437   }
    00438 }
    00439
    00445 static uint GetAcceptanceMask(const Station *st)
    00446 {
    00447   uint mask = 0;
    00448
    00449   for (CargoID i = 0; i < NUM_CARGO; i++) {
    00450     if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::GES_ACCEPTANCE)) mask |= 1 << i;
    00451   }
    00452   return mask;
    00453 }
    00454
    00459 static void ShowRejectOrAcceptNews(const Station *st, uint num_items, CargoID *cargo, StringID msg)
    00460 {
    00461   for (uint i = 0; i < num_items; i++) {
    00462     SetDParam(i + 1, CargoSpec::Get(cargo[i])->name);
    00463   }
    00464
    00465   SetDParam(0, st->index);
    00466   AddNewsItem(msg, NS_ACCEPTANCE, NR_STATION, st->index);
    00467 }
    00468
    00476 CargoArray GetProductionAroundTiles(TileIndex tile, int w, int h, int rad)
    00477 {
    00478   CargoArray produced;
    00479
    00480   int x = TileX(tile);
    00481   int y = TileY(tile);
    00482
    00483   /* expand the region by rad tiles on each side
    00484    * while making sure that we remain inside the board. */
    00485   int x2 = min(x + w + rad, MapSizeX());
    00486   int x1 = max(x - rad, 0);
    00487
    00488   int y2 = min(y + h + rad, MapSizeY());
    00489   int y1 = max(y - rad, 0);
    00490
    00491   assert(x1 < x2);
    00492   assert(y1 < y2);
    00493   assert(w > 0);
    00494   assert(h > 0);
    00495
    00496   TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
    00497
    00498   /* Loop over all tiles to get the produced cargo of
    00499    * everything except industries */
    00500   TILE_AREA_LOOP(tile, ta) AddProducedCargo(tile, produced);
    00501
    00502   /* Loop over the industries. They produce cargo for
    00503    * anything that is within 'rad' from their bounding
    00504    * box. As such if you have e.g. a oil well the tile
    00505    * area loop might not hit an industry tile while
    00506    * the industry would produce cargo for the station.
    00507    */
    00508   const Industry *i;
    00509   FOR_ALL_INDUSTRIES(i) {
    00510     if (!ta.Intersects(i->location)) continue;
    00511
    00512     for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
    00513       CargoID cargo = i->produced_cargo[j];
    00514       if (cargo != CT_INVALID) produced[cargo]++;
    00515     }
    00516   }
    00517
    00518   return produced;
    00519 }
    00520
    00529 CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, uint32 *always_accepted)
    00530 {
    00531   CargoArray acceptance;
    00532   if (always_accepted != NULL) *always_accepted = 0;
    00533
    00534   int x = TileX(tile);
    00535   int y = TileY(tile);
    00536
    00537   /* expand the region by rad tiles on each side
    00538    * while making sure that we remain inside the board. */
    00539   int x2 = min(x + w + rad, MapSizeX());
    00540   int y2 = min(y + h + rad, MapSizeY());
    00541   int x1 = max(x - rad, 0);
    00542   int y1 = max(y - rad, 0);
    00543
    00544   assert(x1 < x2);
    00545   assert(y1 < y2);
    00546   assert(w > 0);
    00547   assert(h > 0);
    00548
    00549   for (int yc = y1; yc != y2; yc++) {
    00550     for (int xc = x1; xc != x2; xc++) {
    00551       TileIndex tile = TileXY(xc, yc);
    00552       AddAcceptedCargo(tile, acceptance, always_accepted);
    00553     }
    00554   }
    00555
    00556   return acceptance;
    00557 }
    00558
    00564 void UpdateStationAcceptance(Station *st, bool show_msg)
    00565 {
    00566   /* old accepted goods types */
    00567   uint old_acc = GetAcceptanceMask(st);
    00568
    00569   /* And retrieve the acceptance. */
    00570   CargoArray acceptance;
    00571   if (!st->rect.IsEmpty()) {
    00572     acceptance = GetAcceptanceAroundTiles(
    00573       TileXY(st->rect.left, st->rect.top),
    00574       st->rect.right  - st->rect.left + 1,
    00575       st->rect.bottom - st->rect.top  + 1,
    00576       st->GetCatchmentRadius(),
    00577       &st->always_accepted
    00578     );
    00579   }
    00580
    00581   /* Adjust in case our station only accepts fewer kinds of goods */
    00582   for (CargoID i = 0; i < NUM_CARGO; i++) {
    00583     uint amt = min(acceptance[i], 15);
    00584
    00585     /* Make sure the station can accept the goods type. */
    00586     bool is_passengers = IsCargoInClass(i, CC_PASSENGERS);
    00587     if ((!is_passengers && !(st->facilities & ~FACIL_BUS_STOP)) ||
    00588         (is_passengers && !(st->facilities & ~FACIL_TRUCK_STOP))) {
    00589       amt = 0;
    00590     }
    00591
    00592     SB(st->goods[i].acceptance_pickup, GoodsEntry::GES_ACCEPTANCE, 1, amt >= 8);
    00593   }
    00594
    00595   /* Only show a message in case the acceptance was actually changed. */
    00596   uint new_acc = GetAcceptanceMask(st);
    00597   if (old_acc == new_acc) return;
    00598
    00599   /* show a message to report that the acceptance was changed? */
    00600   if (show_msg && st->owner == _local_company && st->IsInUse()) {
    00601     /* List of accept and reject strings for different number of
    00602      * cargo types */
    00603     static const StringID accept_msg[] = {
    00604       STR_NEWS_STATION_NOW_ACCEPTS_CARGO,
    00605       STR_NEWS_STATION_NOW_ACCEPTS_CARGO_AND_CARGO,
    00606     };
    00607     static const StringID reject_msg[] = {
    00608       STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO,
    00609       STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_OR_CARGO,
    00610     };
    00611
    00612     /* Array of accepted and rejected cargo types */
    00613     CargoID accepts[2] = { CT_INVALID, CT_INVALID };
    00614     CargoID rejects[2] = { CT_INVALID, CT_INVALID };
    00615     uint num_acc = 0;
    00616     uint num_rej = 0;
    00617
    00618     /* Test each cargo type to see if its acceptange has changed */
    00619     for (CargoID i = 0; i < NUM_CARGO; i++) {
    00620       if (HasBit(new_acc, i)) {
    00621         if (!HasBit(old_acc, i) && num_acc < lengthof(accepts)) {
    00622           /* New cargo is accepted */
    00623           accepts[num_acc++] = i;
    00624         }
    00625       } else {
    00626         if (HasBit(old_acc, i) && num_rej < lengthof(rejects)) {
    00627           /* Old cargo is no longer accepted */
    00628           rejects[num_rej++] = i;
    00629         }
    00630       }
    00631     }
    00632
    00633     /* Show news message if there are any changes */
    00634     if (num_acc > 0) ShowRejectOrAcceptNews(st, num_acc, accepts, accept_msg[num_acc - 1]);
    00635     if (num_rej > 0) ShowRejectOrAcceptNews(st, num_rej, rejects, reject_msg[num_rej - 1]);
    00636   }
    00637
    00638   /* redraw the station view since acceptance changed */
    00639   SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_ACCEPTLIST);
    00640 }
    00641
    00642 static void UpdateStationSignCoord(BaseStation *st)
    00643 {
    00644   const StationRect *r = &st->rect;
    00645
    00646   if (r->IsEmpty()) return; // no tiles belong to this station
    00647
    00648   /* clamp sign coord to be inside the station rect */
    00649   st->xy = TileXY(ClampU(TileX(st->xy), r->left, r->right), ClampU(TileY(st->xy), r->top, r->bottom));
    00650   st->UpdateVirtCoord();
    00651 }
    00652
    00659 static void DeleteStationIfEmpty(BaseStation *st)
    00660 {
    00661   if (!st->IsInUse()) {
    00662     st->delete_ctr = 0;
    00663     InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
    00664   }
    00665   /* station remains but it probably lost some parts - station sign should stay in the station boundaries */
    00666   UpdateStationSignCoord(st);
    00667 }
    00668
    00669 CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags);
    00670
    00679 CommandCost CheckBuildableTile(TileIndex tile, uint invalid_dirs, int &allowed_z, bool check_bridge = true)
    00680 {
    00681   if (check_bridge && MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) {
    00682     return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
    00683   }
    00684
    00685   CommandCost ret = EnsureNoVehicleOnGround(tile);
    00686   if (ret.Failed()) return ret;
    00687
    00688   uint z;
    00689   Slope tileh = GetTileSlope(tile, &z);
    00690
    00691   /* Prohibit building if
    00692    *   1) The tile is "steep" (i.e. stretches two height levels).
    00693    *   2) The tile is non-flat and the build_on_slopes switch is disabled.
    00694    */
    00695   if (IsSteepSlope(tileh) ||
    00696       ((!_settings_game.construction.build_on_slopes) && tileh != SLOPE_FLAT)) {
    00697     return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
    00698   }
    00699
    00700   CommandCost cost(EXPENSES_CONSTRUCTION);
    00701   int flat_z = z;
    00702   if (tileh != SLOPE_FLAT) {
    00703     /* Forbid building if the tile faces a slope in a invalid direction. */
    00704     if ((HasBit(invalid_dirs, DIAGDIR_NE) && !(tileh & SLOPE_NE)) ||
    00705         (HasBit(invalid_dirs, DIAGDIR_SE) && !(tileh & SLOPE_SE)) ||
    00706         (HasBit(invalid_dirs, DIAGDIR_SW) && !(tileh & SLOPE_SW)) ||
    00707         (HasBit(invalid_dirs, DIAGDIR_NW) && !(tileh & SLOPE_NW))) {
    00708       return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
    00709     }
    00710     cost.AddCost(_price[PR_BUILD_FOUNDATION]);
    00711     flat_z += TILE_HEIGHT;
    00712   }
    00713
    00714   /* The level of this tile must be equal to allowed_z. */
    00715   if (allowed_z < 0) {
    00716     /* First tile. */
    00717     allowed_z = flat_z;
    00718   } else if (allowed_z != flat_z) {
    00719     return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
    00720   }
    00721
    00722   return cost;
    00723 }
    00724
    00731 CommandCost CheckFlatLand(TileArea tile_area, DoCommandFlag flags)
    00732 {
    00733   CommandCost cost(EXPENSES_CONSTRUCTION);
    00734   int allowed_z = -1;
    00735
    00736   TILE_AREA_LOOP(tile_cur, tile_area) {
    00737     CommandCost ret = CheckBuildableTile(tile_cur, 0, allowed_z);
    00738     if (ret.Failed()) return ret;
    00739     cost.AddCost(ret);
    00740
    00741     ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    00742     if (ret.Failed()) return ret;
    00743     cost.AddCost(ret);
    00744   }
    00745
    00746   return cost;
    00747 }
    00748
    00759 static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, StationID *station, RailType rt, SmallVector<Train *, 4> &affected_vehicles)
    00760 {
    00761   CommandCost cost(EXPENSES_CONSTRUCTION);
    00762   int allowed_z = -1;
    00763
    00764   TILE_AREA_LOOP(tile_cur, tile_area) {
    00765     CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z);
    00766     if (ret.Failed()) return ret;
    00767     cost.AddCost(ret);
    00768
    00769     /* if station is set, then we have special handling to allow building on top of already existing stations.
    00770      * so station points to INVALID_STATION if we can build on any station.
    00771      * Or it points to a station if we're only allowed to build on exactly that station. */
    00772     if (station != NULL && IsTileType(tile_cur, MP_STATION)) {
    00773       if (!IsRailStation(tile_cur)) {
    00774         return ClearTile_Station(tile_cur, DC_AUTO); // get error message
    00775       } else {
    00776         StationID st = GetStationIndex(tile_cur);
    00777         if (*station == INVALID_STATION) {
    00778           *station = st;
    00779         } else if (*station != st) {
    00780           return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
    00781         }
    00782       }
    00783     } else {
    00784       /* Rail type is only valid when building a railway station; if station to
    00785        * build isn't a rail station it's INVALID_RAILTYPE. */
    00786       if (rt != INVALID_RAILTYPE &&
    00787           IsPlainRailTile(tile_cur) && !HasSignals(tile_cur) &&
    00788           HasPowerOnRail(GetRailType(tile_cur), rt)) {
    00789         /* Allow overbuilding if the tile:
    00790          *  - has rail, but no signals
    00791          *  - it has exactly one track
    00792          *  - the track is in line with the station
    00793          *  - the current rail type has power on the to-be-built type (e.g. convert normal rail to el rail)
    00794          */
    00795         TrackBits tracks = GetTrackBits(tile_cur);
    00796         Track track = RemoveFirstTrack(&tracks);
    00797         Track expected_track = HasBit(invalid_dirs, DIAGDIR_NE) ? TRACK_X : TRACK_Y;
    00798
    00799         if (tracks == TRACK_BIT_NONE && track == expected_track) {
    00800           /* Check for trains having a reservation for this tile. */
    00801           if (HasBit(GetRailReservationTrackBits(tile_cur), track)) {
    00802             Train *v = GetTrainForReservation(tile_cur, track);
    00803             if (v != NULL) {
    00804               *affected_vehicles.Append() = v;
    00805             }
    00806           }
    00807           CommandCost ret = DoCommand(tile_cur, 0, track, flags, CMD_REMOVE_SINGLE_RAIL);
    00808           if (ret.Failed()) return ret;
    00809           cost.AddCost(ret);
    00810           /* With flags & ~DC_EXEC CmdLandscapeClear would fail since the rail still exists */
    00811           continue;
    00812         }
    00813       }
    00814       ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    00815       if (ret.Failed()) return ret;
    00816       cost.AddCost(ret);
    00817     }
    00818   }
    00819
    00820   return cost;
    00821 }
    00822
    00835 static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, bool is_truck_stop, Axis axis, StationID *station, RoadTypes rts)
    00836 {
    00837   CommandCost cost(EXPENSES_CONSTRUCTION);
    00838   int allowed_z = -1;
    00839
    00840   TILE_AREA_LOOP(cur_tile, tile_area) {
    00841     CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z);
    00842     if (ret.Failed()) return ret;
    00843     cost.AddCost(ret);
    00844
    00845     /* If station is set, then we have special handling to allow building on top of already existing stations.
    00846      * Station points to INVALID_STATION if we can build on any station.
    00847      * Or it points to a station if we're only allowed to build on exactly that station. */
    00848     if (station != NULL && IsTileType(cur_tile, MP_STATION)) {
    00849       if (!IsRoadStop(cur_tile)) {
    00850         return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
    00851       } else {
    00852         if (is_truck_stop != IsTruckStop(cur_tile) ||
    00853             is_drive_through != IsDriveThroughStopTile(cur_tile)) {
    00854           return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
    00855         }
    00856         /* Drive-through station in the wrong direction. */
    00857         if (is_drive_through && IsDriveThroughStopTile(cur_tile) && DiagDirToAxis(GetRoadStopDir(cur_tile)) != axis){
    00858           return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
    00859         }
    00860         StationID st = GetStationIndex(cur_tile);
    00861         if (*station == INVALID_STATION) {
    00862           *station = st;
    00863         } else if (*station != st) {
    00864           return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
    00865         }
    00866       }
    00867     } else {
    00868       bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
    00869       /* Road bits in the wrong direction. */
    00870       RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
    00871       if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
    00872         /* Someone was pedantic and *NEEDED* three fracking different error messages. */
    00873         switch (CountBits(rb)) {
    00874           case 1:
    00875             return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
    00876
    00877           case 2:
    00878             if (rb == ROAD_X || rb == ROAD_Y) return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
    00879             return_cmd_error(STR_ERROR_DRIVE_THROUGH_CORNER);
    00880
    00881           default: // 3 or 4
    00882             return_cmd_error(STR_ERROR_DRIVE_THROUGH_JUNCTION);
    00883         }
    00884       }
    00885
    00886       RoadTypes cur_rts = IsNormalRoadTile(cur_tile) ? GetRoadTypes(cur_tile) : ROADTYPES_NONE;
    00887       uint num_roadbits = 0;
    00888       if (build_over_road) {
    00889         /* There is a road, check if we can build road+tram stop over it. */
    00890         if (HasBit(cur_rts, ROADTYPE_ROAD)) {
    00891           Owner road_owner = GetRoadOwner(cur_tile, ROADTYPE_ROAD);
    00892           if (road_owner == OWNER_TOWN) {
    00893             if (!_settings_game.construction.road_stop_on_town_road) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
    00894           } else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) {
    00895             CommandCost ret = CheckOwnership(road_owner);
    00896             if (ret.Failed()) return ret;
    00897           }
    00898           num_roadbits += CountBits(GetRoadBits(cur_tile, ROADTYPE_ROAD));
    00899         }
    00900
    00901         /* There is a tram, check if we can build road+tram stop over it. */
    00902         if (HasBit(cur_rts, ROADTYPE_TRAM)) {
    00903           Owner tram_owner = GetRoadOwner(cur_tile, ROADTYPE_TRAM);
    00904           if (!_settings_game.construction.road_stop_on_competitor_road && tram_owner != OWNER_NONE) {
    00905             CommandCost ret = CheckOwnership(tram_owner);
    00906             if (ret.Failed()) return ret;
    00907           }
    00908           num_roadbits += CountBits(GetRoadBits(cur_tile, ROADTYPE_TRAM));
    00909         }
    00910
    00911         /* Take into account existing roadbits. */
    00912         rts |= cur_rts;
    00913       } else {
    00914         ret = DoCommand(cur_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    00915         if (ret.Failed()) return ret;
    00916         cost.AddCost(ret);
    00917       }
    00918
    00919       uint roadbits_to_build = CountBits(rts) * 2 - num_roadbits;
    00920       cost.AddCost(_price[PR_BUILD_ROAD] * roadbits_to_build);
    00921     }
    00922   }
    00923
    00924   return cost;
    00925 }
    00926
    00934 CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta, Axis axis)
    00935 {
    00936   TileArea cur_ta = st->train_station;
    00937
    00938   /* determine new size of train station region.. */
    00939   int x = min(TileX(cur_ta.tile), TileX(new_ta.tile));
    00940   int y = min(TileY(cur_ta.tile), TileY(new_ta.tile));
    00941   new_ta.w = max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
    00942   new_ta.h = max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
    00943   new_ta.tile = TileXY(x, y);
    00944
    00945   /* make sure the final size is not too big. */
    00946   if (new_ta.w > _settings_game.station.station_spread || new_ta.h > _settings_game.station.station_spread) {
    00947     return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT);
    00948   }
    00949
    00950   return CommandCost();
    00951 }
    00952
    00953 static inline byte *CreateSingle(byte *layout, int n)
    00954 {
    00955   int i = n;
    00956   do *layout++ = 0; while (--i);
    00957   layout[((n - 1) >> 1) - n] = 2;
    00958   return layout;
    00959 }
    00960
    00961 static inline byte *CreateMulti(byte *layout, int n, byte b)
    00962 {
    00963   int i = n;
    00964   do *layout++ = b; while (--i);
    00965   if (n > 4) {
    00966     layout[0 - n] = 0;
    00967     layout[n - 1 - n] = 0;
    00968   }
    00969   return layout;
    00970 }
    00971
    00979 void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec)
    00980 {
    00981   if (statspec != NULL && statspec->lengths >= plat_len &&
    00982       statspec->platforms[plat_len - 1] >= numtracks &&
    00983       statspec->layouts[plat_len - 1][numtracks - 1]) {
    00984     /* Custom layout defined, follow it. */
    00985     memcpy(layout, statspec->layouts[plat_len - 1][numtracks - 1],
    00986       plat_len * numtracks);
    00987     return;
    00988   }
    00989
    00990   if (plat_len == 1) {
    00991     CreateSingle(layout, numtracks);
    00992   } else {
    00993     if (numtracks & 1) layout = CreateSingle(layout, plat_len);
    00994     numtracks >>= 1;
    00995
    00996     while (--numtracks >= 0) {
    00997       layout = CreateMulti(layout, plat_len, 4);
    00998       layout = CreateMulti(layout, plat_len, 6);
    00999     }
    01000   }
    01001 }
    01002
    01014 template <class T, StringID error_message>
    01015 CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st)
    01016 {
    01017   assert(*st == NULL);
    01018   bool check_surrounding = true;
    01019
    01020   if (_settings_game.station.adjacent_stations) {
    01021     if (existing_station != INVALID_STATION) {
    01022       if (adjacent && existing_station != station_to_join) {
    01023         /* You can't build an adjacent station over the top of one that
    01024          * already exists. */
    01025         return_cmd_error(error_message);
    01026       } else {
    01027         /* Extend the current station, and don't check whether it will
    01028          * be near any other stations. */
    01029         *st = T::GetIfValid(existing_station);
    01030         check_surrounding = (*st == NULL);
    01031       }
    01032     } else {
    01033       /* There's no station here. Don't check the tiles surrounding this
    01034        * one if the company wanted to build an adjacent station. */
    01035       if (adjacent) check_surrounding = false;
    01036     }
    01037   }
    01038
    01039   if (check_surrounding) {
    01040     /* Make sure there are no similar stations around us. */
    01041     CommandCost ret = GetStationAround(ta, existing_station, st);
    01042     if (ret.Failed()) return ret;
    01043   }
    01044
    01045   /* Distant join */
    01046   if (*st == NULL && station_to_join != INVALID_STATION) *st = T::GetIfValid(station_to_join);
    01047
    01048   return CommandCost();
    01049 }
    01050
    01060 static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
    01061 {
    01062   return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st);
    01063 }
    01064
    01074 CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp)
    01075 {
    01076   return FindJoiningBaseStation<Waypoint, STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp);
    01077 }
    01078
    01096 CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    01097 {
    01098   /* Unpack parameters */
    01099   RailType rt    = Extract<RailType, 0, 4>(p1);
    01100   Axis axis      = Extract<Axis, 4, 1>(p1);
    01101   byte numtracks = GB(p1,  8, 8);
    01102   byte plat_len  = GB(p1, 16, 8);
    01103   bool adjacent  = HasBit(p1, 24);
    01104
    01105   StationClassID spec_class = Extract<StationClassID, 0, 8>(p2);
    01106   byte spec_index           = GB(p2, 8, 8);
    01107   StationID station_to_join = GB(p2, 16, 16);
    01108
    01109   /* Does the authority allow this? */
    01110   CommandCost ret = CheckIfAuthorityAllowsNewStation(tile_org, flags);
    01111   if (ret.Failed()) return ret;
    01112
    01113   if (!ValParamRailtype(rt)) return CMD_ERROR;
    01114
    01115   /* Check if the given station class is valid */
    01116   if ((uint)spec_class >= StationClass::GetCount() || spec_class == STAT_CLASS_WAYP) return CMD_ERROR;
    01117   if (spec_index >= StationClass::GetCount(spec_class)) return CMD_ERROR;
    01118   if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
    01119
    01120   int w_org, h_org;
    01121   if (axis == AXIS_X) {
    01122     w_org = plat_len;
    01123     h_org = numtracks;
    01124   } else {
    01125     h_org = plat_len;
    01126     w_org = numtracks;
    01127   }
    01128
    01129   bool reuse = (station_to_join != NEW_STATION);
    01130   if (!reuse) station_to_join = INVALID_STATION;
    01131   bool distant_join = (station_to_join != INVALID_STATION);
    01132
    01133   if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
    01134
    01135   if (h_org > _settings_game.station.station_spread || w_org > _settings_game.station.station_spread) return CMD_ERROR;
    01136
    01137   /* these values are those that will be stored in train_tile and station_platforms */
    01138   TileArea new_location(tile_org, w_org, h_org);
    01139
    01140   /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
    01141   StationID est = INVALID_STATION;
    01142   SmallVector<Train *, 4> affected_vehicles;
    01143   /* Clear the land below the station. */
    01144   CommandCost cost = CheckFlatLandRailStation(TileArea(tile_org, w_org, h_org), flags, 5 << axis, &est, rt, affected_vehicles);
    01145   if (cost.Failed()) return cost;
    01146   /* Add construction expenses. */
    01147   cost.AddCost((numtracks * _price[PR_BUILD_STATION_RAIL] + _price[PR_BUILD_STATION_RAIL_LENGTH]) * plat_len);
    01148   cost.AddCost(numtracks * plat_len * RailBuildCost(rt));
    01149
    01150   Station *st = NULL;
    01151   ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
    01152   if (ret.Failed()) return ret;
    01153
    01154   /* See if there is a deleted station close to us. */
    01155   if (st == NULL && reuse) st = GetClosestDeletedStation(tile_org);
    01156
    01157   if (st != NULL) {
    01158     /* Reuse an existing station. */
    01159     if (st->owner != _current_company) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
    01160
    01161     if (st->train_station.tile != INVALID_TILE) {
    01162       CommandCost ret = CanExpandRailStation(st, new_location, axis);
    01163       if (ret.Failed()) return ret;
    01164     }
    01165
    01166     /* XXX can't we pack this in the "else" part of the if above? */
    01167     CommandCost ret = st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TEST);
    01168     if (ret.Failed()) return ret;
    01169   } else {
    01170     /* allocate and initialize new station */
    01171     if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
    01172
    01173     if (flags & DC_EXEC) {
    01174       st = new Station(tile_org);
    01175
    01176       st->town = ClosestTownFromTile(tile_org, UINT_MAX);
    01177       st->string_id = GenerateStationName(st, tile_org, STATIONNAMING_RAIL);
    01178
    01179       if (Company::IsValidID(_current_company)) {
    01180         SetBit(st->town->have_ratings, _current_company);
    01181       }
    01182     }
    01183   }
    01184
    01185   /* Check if we can allocate a custom stationspec to this station */
    01186   const StationSpec *statspec = StationClass::Get(spec_class, spec_index);
    01187   int specindex = AllocateSpecToStation(statspec, st, (flags & DC_EXEC) != 0);
    01188   if (specindex == -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS);
    01189
    01190   if (statspec != NULL) {
    01191     /* Perform NewStation checks */
    01192
    01193     /* Check if the station size is permitted */
    01194     if (HasBit(statspec->disallowed_platforms, numtracks - 1) || HasBit(statspec->disallowed_lengths, plat_len - 1)) {
    01195       return CMD_ERROR;
    01196     }
    01197
    01198     /* Check if the station is buildable */
    01199     if (HasBit(statspec->callback_mask, CBM_STATION_AVAIL) && GB(GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE), 0, 8) == 0) {
    01200       return CMD_ERROR;
    01201     }
    01202   }
    01203
    01204   if (flags & DC_EXEC) {
    01205     TileIndexDiff tile_delta;
    01206     byte *layout_ptr;
    01207     byte numtracks_orig;
    01208     Track track;
    01209
    01210     st->train_station = new_location;
    01211     st->AddFacility(FACIL_TRAIN, new_location.tile);
    01212
    01213     st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
    01214
    01215     if (statspec != NULL) {
    01216       /* Include this station spec's animation trigger bitmask
    01217        * in the station's cached copy. */
    01218       st->cached_anim_triggers |= statspec->animation.triggers;
    01219     }
    01220
    01221     tile_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
    01222     track = AxisToTrack(axis);
    01223
    01224     layout_ptr = AllocaM(byte, numtracks * plat_len);
    01225     GetStationLayout(layout_ptr, numtracks, plat_len, statspec);
    01226
    01227     numtracks_orig = numtracks;
    01228
    01229     do {
    01230       TileIndex tile = tile_org;
    01231       int w = plat_len;
    01232       do {
    01233         byte layout = *layout_ptr++;
    01234         if (IsRailStationTile(tile) && HasStationReservation(tile)) {
    01235           /* Check for trains having a reservation for this tile. */
    01236           Train *v = GetTrainForReservation(tile, AxisToTrack(GetRailStationAxis(tile)));
    01237           if (v != NULL) {
    01238             FreeTrainTrackReservation(v);
    01239             *affected_vehicles.Append() = v;
    01240             if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), false);
    01241             for (; v->Next() != NULL; v = v->Next()) { }
    01242             if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), false);
    01243           }
    01244         }
    01245
    01246         /* Remove animation if overbuilding */
    01247         DeleteAnimatedTile(tile);
    01248         byte old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
    01249         MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, rt);
    01250         /* Free the spec if we overbuild something */
    01251         DeallocateSpecFromStation(st, old_specindex);
    01252
    01253         SetCustomStationSpecIndex(tile, specindex);
    01254         SetStationTileRandomBits(tile, GB(Random(), 0, 4));
    01255         SetAnimationFrame(tile, 0);
    01256
    01257         if (statspec != NULL) {
    01258           /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
    01259           uint32 platinfo = GetPlatformInfo(AXIS_X, 0, plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
    01260
    01261           /* As the station is not yet completely finished, the station does not yet exist. */
    01262           uint16 callback = GetStationCallback(CBID_STATION_TILE_LAYOUT, platinfo, 0, statspec, NULL, tile);
    01263           if (callback != CALLBACK_FAILED && callback < 8) SetStationGfx(tile, (callback & ~1) + axis);
    01264
    01265           /* Trigger station animation -- after building? */
    01266           TriggerStationAnimation(st, tile, SAT_BUILT);
    01267         }
    01268
    01269         tile += tile_delta;
    01270       } while (--w);
    01271       AddTrackToSignalBuffer(tile_org, track, _current_company);
    01272       YapfNotifyTrackLayoutChange(tile_org, track);
    01273       tile_org += tile_delta ^ TileDiffXY(1, 1); // perpendicular to tile_delta
    01274     } while (--numtracks);
    01275
    01276     for (uint i = 0; i < affected_vehicles.Length(); ++i) {
    01277       /* Restore reservations of trains. */
    01278       Train *v = affected_vehicles[i];
    01279       if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true);
    01280       TryPathReserve(v, true, true);
    01281       for (; v->Next() != NULL; v = v->Next()) { }
    01282       if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), true);
    01283     }
    01284
    01285     st->MarkTilesDirty(false);
    01286     st->UpdateVirtCoord();
    01287     UpdateStationAcceptance(st, false);
    01288     st->RecomputeIndustriesNear();
    01289     InvalidateWindowData(WC_SELECT_STATION, 0, 0);
    01290     InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
    01291     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
    01292   }
    01293
    01294   return cost;
    01295 }
    01296
    01297 static void MakeRailStationAreaSmaller(BaseStation *st)
    01298 {
    01299   TileArea ta = st->train_station;
    01300
    01301 restart:
    01302
    01303   /* too small? */
    01304   if (ta.w != 0 && ta.h != 0) {
    01305     /* check the left side, x = constant, y changes */
    01306     for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(0, i));) {
    01307       /* the left side is unused? */
    01308       if (++i == ta.h) {
    01309         ta.tile += TileDiffXY(1, 0);
    01310         ta.w--;
    01311         goto restart;
    01312       }
    01313     }
    01314
    01315     /* check the right side, x = constant, y changes */
    01316     for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(ta.w - 1, i));) {
    01317       /* the right side is unused? */
    01318       if (++i == ta.h) {
    01319         ta.w--;
    01320         goto restart;
    01321       }
    01322     }
    01323
    01324     /* check the upper side, y = constant, x changes */
    01325     for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(i, 0));) {
    01326       /* the left side is unused? */
    01327       if (++i == ta.w) {
    01328         ta.tile += TileDiffXY(0, 1);
    01329         ta.h--;
    01330         goto restart;
    01331       }
    01332     }
    01333
    01334     /* check the lower side, y = constant, x changes */
    01335     for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(i, ta.h - 1));) {
    01336       /* the left side is unused? */
    01337       if (++i == ta.w) {
    01338         ta.h--;
    01339         goto restart;
    01340       }
    01341     }
    01342   } else {
    01343     ta.Clear();
    01344   }
    01345
    01346   st->train_station = ta;
    01347 }
    01348
    01359 template <class T>
    01360 CommandCost RemoveFromRailBaseStation(TileArea ta, SmallVector<T *, 4> &affected_stations, DoCommandFlag flags, Money removal_cost, bool keep_rail)
    01361 {
    01362   /* Count of the number of tiles removed */
    01363   int quantity = 0;
    01364   CommandCost total_cost(EXPENSES_CONSTRUCTION);
    01365
    01366   /* Do the action for every tile into the area */
    01367   TILE_AREA_LOOP(tile, ta) {
    01368     /* Make sure the specified tile is a rail station */
    01369     if (!HasStationTileRail(tile)) continue;
    01370
    01371     /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
    01372     CommandCost ret = EnsureNoVehicleOnGround(tile);
    01373     if (ret.Failed()) continue;
    01374
    01375     /* Check ownership of station */
    01376     T *st = T::GetByTile(tile);
    01377     if (st == NULL) continue;
    01378
    01379     if (_current_company != OWNER_WATER) {
    01380       CommandCost ret = CheckOwnership(st->owner);
    01381       if (ret.Failed()) continue;
    01382     }
    01383
    01384     /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
    01385     quantity++;
    01386
    01387     if (keep_rail || IsStationTileBlocked(tile)) {
    01388       /* Don't refund the 'steel' of the track when we keep the
    01389        *  rail, or when the tile didn't have any rail at all. */
    01390       total_cost.AddCost(-_price[PR_CLEAR_RAIL]);
    01391     }
    01392
    01393     if (flags & DC_EXEC) {
    01394       /* read variables before the station tile is removed */
    01395       uint specindex = GetCustomStationSpecIndex(tile);
    01396       Track track = GetRailStationTrack(tile);
    01397       Owner owner = GetTileOwner(tile);
    01398       RailType rt = GetRailType(tile);
    01399       Train *v = NULL;
    01400
    01401       if (HasStationReservation(tile)) {
    01402         v = GetTrainForReservation(tile, track);
    01403         if (v != NULL) {
    01404           /* Free train reservation. */
    01405           FreeTrainTrackReservation(v);
    01406           if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), false);
    01407           Vehicle *temp = v;
    01408           for (; temp->Next() != NULL; temp = temp->Next()) { }
    01409           if (IsRailStationTile(temp->tile)) SetRailStationPlatformReservation(temp->tile, TrackdirToExitdir(ReverseTrackdir(temp->GetVehicleTrackdir())), false);
    01410         }
    01411       }
    01412
    01413       bool build_rail = keep_rail && !IsStationTileBlocked(tile);
    01414
    01415       DoClearSquare(tile);
    01416       DeleteNewGRFInspectWindow(GSF_STATIONS, tile);
    01417       if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt);
    01418
    01419       st->rect.AfterRemoveTile(st, tile);
    01420       AddTrackToSignalBuffer(tile, track, owner);
    01421       YapfNotifyTrackLayoutChange(tile, track);
    01422
    01423       DeallocateSpecFromStation(st, specindex);
    01424
    01425       affected_stations.Include(st);
    01426
    01427       if (v != NULL) {
    01428         /* Restore station reservation. */
    01429         if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true);
    01430         TryPathReserve(v, true, true);
    01431         for (; v->Next() != NULL; v = v->Next()) { }
    01432         if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), true);
    01433       }
    01434     }
    01435   }
    01436
    01437   if (quantity == 0) return_cmd_error(STR_ERROR_THERE_IS_NO_STATION);
    01438
    01439   for (T **stp = affected_stations.Begin(); stp != affected_stations.End(); stp++) {
    01440     T *st = *stp;
    01441
    01442     /* now we need to make the "spanned" area of the railway station smaller
    01443      * if we deleted something at the edges.
    01444      * we also need to adjust train_tile. */
    01445     MakeRailStationAreaSmaller(st);
    01446     UpdateStationSignCoord(st);
    01447
    01448     /* if we deleted the whole station, delete the train facility. */
    01449     if (st->train_station.tile == INVALID_TILE) {
    01450       st->facilities &= ~FACIL_TRAIN;
    01451       SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
    01452       st->UpdateVirtCoord();
    01453       DeleteStationIfEmpty(st);
    01454     }
    01455   }
    01456
    01457   total_cost.AddCost(quantity * removal_cost);
    01458   return total_cost;
    01459 }
    01460
    01472 CommandCost CmdRemoveFromRailStation(TileIndex start, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    01473 {
    01474   TileIndex end = p1 == 0 ? start : p1;
    01475   if (start >= MapSize() || end >= MapSize()) return CMD_ERROR;
    01476
    01477   TileArea ta(start, end);
    01478   SmallVector<Station *, 4> affected_stations;
    01479
    01480   CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_STATION_RAIL], HasBit(p2, 0));
    01481   if (ret.Failed()) return ret;
    01482
    01483   /* Do all station specific functions here. */
    01484   for (Station **stp = affected_stations.Begin(); stp != affected_stations.End(); stp++) {
    01485     Station *st = *stp;
    01486
    01487     if (st->train_station.tile == INVALID_TILE) SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
    01488     st->MarkTilesDirty(false);
    01489     st->RecomputeIndustriesNear();
    01490   }
    01491
    01492   /* Now apply the rail cost to the number that we deleted */
    01493   return ret;
    01494 }
    01495
    01507 CommandCost CmdRemoveFromRailWaypoint(TileIndex start, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    01508 {
    01509   TileIndex end = p1 == 0 ? start : p1;
    01510   if (start >= MapSize() || end >= MapSize()) return CMD_ERROR;
    01511
    01512   TileArea ta(start, end);
    01513   SmallVector<Waypoint *, 4> affected_stations;
    01514
    01515   return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_WAYPOINT_RAIL], HasBit(p2, 0));
    01516 }
    01517
    01518
    01526 template <class T>
    01527 CommandCost RemoveRailStation(T *st, DoCommandFlag flags)
    01528 {
    01529   /* Current company owns the station? */
    01530   if (_current_company != OWNER_WATER) {
    01531     CommandCost ret = CheckOwnership(st->owner);
    01532     if (ret.Failed()) return ret;
    01533   }
    01534
    01535   /* determine width and height of platforms */
    01536   TileArea ta = st->train_station;
    01537
    01538   assert(ta.w != 0 && ta.h != 0);
    01539
    01540   CommandCost cost(EXPENSES_CONSTRUCTION);
    01541   /* clear all areas of the station */
    01542   TILE_AREA_LOOP(tile, ta) {
    01543     /* only remove tiles that are actually train station tiles */
    01544     if (!st->TileBelongsToRailStation(tile)) continue;
    01545
    01546     CommandCost ret = EnsureNoVehicleOnGround(tile);
    01547     if (ret.Failed()) return ret;
    01548
    01549     cost.AddCost(_price[PR_CLEAR_STATION_RAIL]);
    01550     if (flags & DC_EXEC) {
    01551       /* read variables before the station tile is removed */
    01552       Track track = GetRailStationTrack(tile);
    01553       Owner owner = GetTileOwner(tile); // _current_company can be OWNER_WATER
    01554       Train *v = NULL;
    01555       if (HasStationReservation(tile)) {
    01556         v = GetTrainForReservation(tile, track);
    01557         if (v != NULL) FreeTrainTrackReservation(v);
    01558       }
    01559       DoClearSquare(tile);
    01560       DeleteNewGRFInspectWindow(GSF_STATIONS, tile);
    01561       AddTrackToSignalBuffer(tile, track, owner);
    01562       YapfNotifyTrackLayoutChange(tile, track);
    01563       if (v != NULL) TryPathReserve(v, true);
    01564     }
    01565   }
    01566
    01567   if (flags & DC_EXEC) {
    01568     st->rect.AfterRemoveRect(st, st->train_station);
    01569
    01570     st->train_station.Clear();
    01571
    01572     st->facilities &= ~FACIL_TRAIN;
    01573
    01574     free(st->speclist);
    01575     st->num_specs = 0;
    01576     st->speclist  = NULL;
    01577     st->cached_anim_triggers = 0;
    01578
    01579     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_TRAINS);
    01580     st->UpdateVirtCoord();
    01581     DeleteStationIfEmpty(st);
    01582   }
    01583
    01584   return cost;
    01585 }
    01586
    01593 static CommandCost RemoveRailStation(TileIndex tile, DoCommandFlag flags)
    01594 {
    01595   /* if there is flooding, remove platforms tile by tile */
    01596   if (_current_company == OWNER_WATER) {
    01597     return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAIL_STATION);
    01598   }
    01599
    01600   Station *st = Station::GetByTile(tile);
    01601   CommandCost cost = RemoveRailStation(st, flags);
    01602
    01603   if (flags & DC_EXEC) st->RecomputeIndustriesNear();
    01604
    01605   return cost;
    01606 }
    01607
    01614 static CommandCost RemoveRailWaypoint(TileIndex tile, DoCommandFlag flags)
    01615 {
    01616   /* if there is flooding, remove waypoints tile by tile */
    01617   if (_current_company == OWNER_WATER) {
    01618     return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAIL_WAYPOINT);
    01619   }
    01620
    01621   return RemoveRailStation(Waypoint::GetByTile(tile), flags);
    01622 }
    01623
    01624
    01630 static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
    01631 {
    01632   RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
    01633
    01634   if (*primary_stop == NULL) {
    01635     /* we have no roadstop of the type yet, so write a "primary stop" */
    01636     return primary_stop;
    01637   } else {
    01638     /* there are stops already, so append to the end of the list */
    01639     RoadStop *stop = *primary_stop;
    01640     while (stop->next != NULL) stop = stop->next;
    01641     return &stop->next;
    01642   }
    01643 }
    01644
    01645 static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags);
    01646
    01656 static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
    01657 {
    01658   return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st);
    01659 }
    01660
    01676 CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    01677 {
    01678   bool type = HasBit(p2, 0);
    01679   bool is_drive_through = HasBit(p2, 1);
    01680   RoadTypes rts = Extract<RoadTypes, 2, 2>(p2);
    01681   StationID station_to_join = GB(p2, 16, 16);
    01682   bool reuse = (station_to_join != NEW_STATION);
    01683   if (!reuse) station_to_join = INVALID_STATION;
    01684   bool distant_join = (station_to_join != INVALID_STATION);
    01685
    01686   uint8 width = (uint8)GB(p1, 0, 8);
    01687   uint8 lenght = (uint8)GB(p1, 8, 8);
    01688
    01689   /* Check if the requested road stop is too big */
    01690   if (width > _settings_game.station.station_spread || lenght > _settings_game.station.station_spread) return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT);
    01691   /* Check for incorrect width / lenght. */
    01692   if (width == 0 || lenght == 0) return CMD_ERROR;
    01693   /* Check if the first tile and the last tile are valid */
    01694   if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, lenght - 1) == INVALID_TILE) return CMD_ERROR;
    01695
    01696   TileArea roadstop_area(tile, width, lenght);
    01697
    01698   if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
    01699
    01700   if (!HasExactlyOneBit(rts) || !HasRoadTypesAvail(_current_company, rts)) return CMD_ERROR;
    01701
    01702   /* Trams only have drive through stops */
    01703   if (!is_drive_through && HasBit(rts, ROADTYPE_TRAM)) return CMD_ERROR;
    01704
    01705   DiagDirection ddir = Extract<DiagDirection, 6, 2>(p2);
    01706
    01707   /* Safeguard the parameters. */
    01708   if (!IsValidDiagDirection(ddir)) return CMD_ERROR;
    01709   /* If it is a drive-through stop, check for valid axis. */
    01710   if (is_drive_through && !IsValidAxis((Axis)ddir)) return CMD_ERROR;
    01711
    01712   CommandCost ret = CheckIfAuthorityAllowsNewStation(tile, flags);
    01713   if (ret.Failed()) return ret;
    01714
    01715   /* Total road stop cost. */
    01716   CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * _price[type ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]);
    01717   StationID est = INVALID_STATION;
    01718   ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << ddir : 1 << ddir, is_drive_through, type, DiagDirToAxis(ddir), &est, rts);
    01719   if (ret.Failed()) return ret;
    01720   cost.AddCost(ret);
    01721
    01722   Station *st = NULL;
    01723   ret = FindJoiningRoadStop(est, station_to_join, HasBit(p2, 5), roadstop_area, &st);
    01724   if (ret.Failed()) return ret;
    01725
    01726   /* Find a deleted station close to us */
    01727   if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
    01728
    01729   /* Check if this number of road stops can be allocated. */
    01730   if (!RoadStop::CanAllocateItem(roadstop_area.w * roadstop_area.h)) return_cmd_error(type ? STR_ERROR_TOO_MANY_TRUCK_STOPS : STR_ERROR_TOO_MANY_BUS_STOPS);
    01731
    01732   if (st != NULL) {
    01733     if (st->owner != _current_company) {
    01734       return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
    01735     }
    01736
    01737     CommandCost ret = st->rect.BeforeAddRect(roadstop_area.tile, roadstop_area.w, roadstop_area.h, StationRect::ADD_TEST);
    01738     if (ret.Failed()) return ret;
    01739   } else {
    01740     /* allocate and initialize new station */
    01741     if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
    01742
    01743     if (flags & DC_EXEC) {
    01744       st = new Station(tile);
    01745
    01746       st->town = ClosestTownFromTile(tile, UINT_MAX);
    01747       st->string_id = GenerateStationName(st, tile, STATIONNAMING_ROAD);
    01748
    01749       if (Company::IsValidID(_current_company)) {
    01750         SetBit(st->town->have_ratings, _current_company);
    01751       }
    01752     }
    01753   }
    01754
    01755   if (flags & DC_EXEC) {
    01756     /* Check every tile in the area. */
    01757     TILE_AREA_LOOP(cur_tile, roadstop_area) {
    01758       RoadTypes cur_rts = GetRoadTypes(cur_tile);
    01759       Owner road_owner = HasBit(cur_rts, ROADTYPE_ROAD) ? GetRoadOwner(cur_tile, ROADTYPE_ROAD) : _current_company;
    01760       Owner tram_owner = HasBit(cur_rts, ROADTYPE_TRAM) ? GetRoadOwner(cur_tile, ROADTYPE_TRAM) : _current_company;
    01761
    01762       if (IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile)) {
    01763         RemoveRoadStop(cur_tile, flags);
    01764       }
    01765
    01766       RoadStop *road_stop = new RoadStop(cur_tile);
    01767       /* Insert into linked list of RoadStops. */
    01768       RoadStop **currstop = FindRoadStopSpot(type, st);
    01769       *currstop = road_stop;
    01770
    01771       if (type) {
    01772         st->truck_station.Add(cur_tile);
    01773       } else {
    01774         st->bus_station.Add(cur_tile);
    01775       }
    01776
    01777       /* Initialize an empty station. */
    01778       st->AddFacility((type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP, cur_tile);
    01779
    01780       st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
    01781
    01782       RoadStopType rs_type = type ? ROADSTOP_TRUCK : ROADSTOP_BUS;
    01783       if (is_drive_through) {
    01784         MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, rs_type, rts | cur_rts, DiagDirToAxis(ddir));
    01785         road_stop->MakeDriveThrough();
    01786       } else {
    01787         MakeRoadStop(cur_tile, st->owner, st->index, rs_type, rts, ddir);
    01788       }
    01789
    01790       MarkTileDirtyByTile(cur_tile);
    01791     }
    01792   }
    01793
    01794   if (st != NULL) {
    01795     st->UpdateVirtCoord();
    01796     UpdateStationAcceptance(st, false);
    01797     st->RecomputeIndustriesNear();
    01798     InvalidateWindowData(WC_SELECT_STATION, 0, 0);
    01799     InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
    01800     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_ROADVEHS);
    01801   }
    01802   return cost;
    01803 }
    01804
    01805
    01806 static Vehicle *ClearRoadStopStatusEnum(Vehicle *v, void *)
    01807 {
    01808   if (v->type == VEH_ROAD) {
    01809     /* Okay... we are a road vehicle on a drive through road stop.
    01810      * But that road stop has just been removed, so we need to make
    01811      * sure we are in a valid state... however, vehicles can also
    01812      * turn on road stop tiles, so only clear the 'road stop' state
    01813      * bits and only when the state was 'in road stop', otherwise
    01814      * we'll end up clearing the turn around bits. */
    01815     RoadVehicle *rv = RoadVehicle::From(v);
    01816     if (HasBit(rv->state, RVS_IN_DT_ROAD_STOP)) rv->state &= RVSB_ROAD_STOP_TRACKDIR_MASK;
    01817   }
    01818
    01819   return NULL;
    01820 }
    01821
    01822
    01829 static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
    01830 {
    01831   Station *st = Station::GetByTile(tile);
    01832
    01833   if (_current_company != OWNER_WATER) {
    01834     CommandCost ret = CheckOwnership(st->owner);
    01835     if (ret.Failed()) return ret;
    01836   }
    01837
    01838   bool is_truck = IsTruckStop(tile);
    01839
    01840   RoadStop **primary_stop;
    01841   RoadStop *cur_stop;
    01842   if (is_truck) { // truck stop
    01843     primary_stop = &st->truck_stops;
    01844     cur_stop = RoadStop::GetByTile(tile, ROADSTOP_TRUCK);
    01845   } else {
    01846     primary_stop = &st->bus_stops;
    01847     cur_stop = RoadStop::GetByTile(tile, ROADSTOP_BUS);
    01848   }
    01849
    01850   assert(cur_stop != NULL);
    01851
    01852   /* don't do the check for drive-through road stops when company bankrupts */
    01853   if (IsDriveThroughStopTile(tile) && (flags & DC_BANKRUPT)) {
    01854     /* remove the 'going through road stop' status from all vehicles on that tile */
    01855     if (flags & DC_EXEC) FindVehicleOnPos(tile, NULL, &ClearRoadStopStatusEnum);
    01856   } else {
    01857     CommandCost ret = EnsureNoVehicleOnGround(tile);
    01858     if (ret.Failed()) return ret;
    01859   }
    01860
    01861   if (flags & DC_EXEC) {
    01862     if (*primary_stop == cur_stop) {
    01863       /* removed the first stop in the list */
    01864       *primary_stop = cur_stop->next;
    01865       /* removed the only stop? */
    01866       if (*primary_stop == NULL) {
    01867         st->facilities &= (is_truck ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP);
    01868       }
    01869     } else {
    01870       /* tell the predecessor in the list to skip this stop */
    01871       RoadStop *pred = *primary_stop;
    01872       while (pred->next != cur_stop) pred = pred->next;
    01873       pred->next = cur_stop->next;
    01874     }
    01875
    01876     if (IsDriveThroughStopTile(tile)) {
    01877       /* Clears the tile for us */
    01878       cur_stop->ClearDriveThrough();
    01879     } else {
    01880       DoClearSquare(tile);
    01881     }
    01882
    01883     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_ROADVEHS);
    01884     delete cur_stop;
    01885
    01886     /* Make sure no vehicle is going to the old roadstop */
    01887     RoadVehicle *v;
    01888     FOR_ALL_ROADVEHICLES(v) {
    01889       if (v->First() == v && v->current_order.IsType(OT_GOTO_STATION) &&
    01890           v->dest_tile == tile) {
    01891         v->dest_tile = v->GetOrderStationLocation(st->index);
    01892       }
    01893     }
    01894
    01895     st->rect.AfterRemoveTile(st, tile);
    01896
    01897     st->UpdateVirtCoord();
    01898     st->RecomputeIndustriesNear();
    01899     DeleteStationIfEmpty(st);
    01900
    01901     /* Update the tile area of the truck/bus stop */
    01902     if (is_truck) {
    01903       st->truck_station.Clear();
    01904       for (const RoadStop *rs = st->truck_stops; rs != NULL; rs = rs->next) st->truck_station.Add(rs->xy);
    01905     } else {
    01906       st->bus_station.Clear();
    01907       for (const RoadStop *rs = st->bus_stops; rs != NULL; rs = rs->next) st->bus_station.Add(rs->xy);
    01908     }
    01909   }
    01910
    01911   return CommandCost(EXPENSES_CONSTRUCTION, _price[is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS]);
    01912 }
    01913
    01924 CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    01925 {
    01926   uint8 width = (uint8)GB(p1, 0, 8);
    01927   uint8 height = (uint8)GB(p1, 8, 8);
    01928
    01929   /* Check for incorrect width / height. */
    01930   if (width == 0 || height == 0) return CMD_ERROR;
    01931   /* Check if the first tile and the last tile are valid */
    01932   if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
    01933
    01934   TileArea roadstop_area(tile, width, height);
    01935
    01936   int quantity = 0;
    01937   CommandCost cost(EXPENSES_CONSTRUCTION);
    01938   TILE_AREA_LOOP(cur_tile, roadstop_area) {
    01939     /* Make sure the specified tile is a road stop of the correct type */
    01940     if (!IsTileType(cur_tile, MP_STATION) || !IsRoadStop(cur_tile) || (uint32)GetRoadStopType(cur_tile) != GB(p2, 0, 1)) continue;
    01941
    01942     /* Save the stop info before it is removed */
    01943     bool is_drive_through = IsDriveThroughStopTile(cur_tile);
    01944     RoadTypes rts = GetRoadTypes(cur_tile);
    01945     RoadBits road_bits = IsDriveThroughStopTile(cur_tile) ?
    01946         ((GetRoadStopDir(cur_tile) == DIAGDIR_NE) ? ROAD_X : ROAD_Y) :
    01947         DiagDirToRoadBits(GetRoadStopDir(cur_tile));
    01948
    01949     Owner road_owner = GetRoadOwner(cur_tile, ROADTYPE_ROAD);
    01950     Owner tram_owner = GetRoadOwner(cur_tile, ROADTYPE_TRAM);
    01951     CommandCost ret = RemoveRoadStop(cur_tile, flags);
    01952     if (ret.Failed()) return ret;
    01953     cost.AddCost(ret);
    01954
    01955     quantity++;
    01956     /* If the stop was a drive-through stop replace the road */
    01957     if ((flags & DC_EXEC) && is_drive_through) {
    01958       MakeRoadNormal(cur_tile, road_bits, rts, ClosestTownFromTile(cur_tile, UINT_MAX)->index,
    01959           road_owner, tram_owner);
    01960     }
    01961   }
    01962
    01963   if (quantity == 0) return_cmd_error(STR_ERROR_THERE_IS_NO_STATION);
    01964
    01965   return cost;
    01966 }
    01967
    01975 static uint GetMinimalAirportDistanceToTile(const AirportSpec *as, TileIndex town_tile, TileIndex airport_tile)
    01976 {
    01977   uint ttx = TileX(town_tile); // X, Y of town
    01978   uint tty = TileY(town_tile);
    01979
    01980   uint atx = TileX(airport_tile); // X, Y of northern airport corner
    01981   uint aty = TileY(airport_tile);
    01982
    01983   uint btx = TileX(airport_tile) + as->size_x - 1; // X, Y of southern corner
    01984   uint bty = TileY(airport_tile) + as->size_y - 1;
    01985
    01986   /* if ttx < atx, dx = atx - ttx
    01987    * if atx <= ttx <= btx, dx = 0
    01988    * else, dx = ttx - btx (similiar for dy) */
    01989   uint dx = ttx < atx ? atx - ttx : (ttx <= btx ? 0 : ttx - btx);
    01990   uint dy = tty < aty ? aty - tty : (tty <= bty ? 0 : tty - bty);
    01991
    01992   return dx + dy;
    01993 }
    01994
    02004 uint8 GetAirportNoiseLevelForTown(const AirportSpec *as, TileIndex town_tile, TileIndex tile)
    02005 {
    02006   /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
    02007    * So no need to go any further*/
    02008   if (as->noise_level < 2) return as->noise_level;
    02009
    02010   uint distance = GetMinimalAirportDistanceToTile(as, town_tile, tile);
    02011
    02012   /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
    02013    * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
    02014    * Basically, it says that the less tolerant a town is, the bigger the distance before
    02015    * an actual decrease can be granted */
    02016   uint8 town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
    02017
    02018   /* now, we want to have the distance segmented using the distance judged bareable by town
    02019    * This will give us the coefficient of reduction the distance provides. */
    02020   uint noise_reduction = distance / town_tolerance_distance;
    02021
    02022   /* If the noise reduction equals the airport noise itself, don't give it for free.
    02023    * Otherwise, simply reduce the airport's level. */
    02024   return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
    02025 }
    02026
    02034 Town *AirportGetNearestTown(const AirportSpec *as, TileIndex airport_tile)
    02035 {
    02036   Town *t, *nearest = NULL;
    02037   uint add = as->size_x + as->size_y - 2; // GetMinimalAirportDistanceToTile can differ from DistanceManhattan by this much
    02038   uint mindist = UINT_MAX - add; // prevent overflow
    02039   FOR_ALL_TOWNS(t) {
    02040     if (DistanceManhattan(t->xy, airport_tile) < mindist + add) { // avoid calling GetMinimalAirportDistanceToTile too often
    02041       uint dist = GetMinimalAirportDistanceToTile(as, t->xy, airport_tile);
    02042       if (dist < mindist) {
    02043         nearest = t;
    02044         mindist = dist;
    02045       }
    02046     }
    02047   }
    02048
    02049   return nearest;
    02050 }
    02051
    02052
    02054 void UpdateAirportsNoise()
    02055 {
    02056   Town *t;
    02057   const Station *st;
    02058
    02059   FOR_ALL_TOWNS(t) t->noise_reached = 0;
    02060
    02061   FOR_ALL_STATIONS(st) {
    02062     if (st->airport.tile != INVALID_TILE) {
    02063       const AirportSpec *as = st->airport.GetSpec();
    02064       Town *nearest = AirportGetNearestTown(as, st->airport.tile);
    02065       nearest->noise_reached += GetAirportNoiseLevelForTown(as, nearest->xy, st->airport.tile);
    02066     }
    02067   }
    02068 }
    02069
    02083 CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    02084 {
    02085   StationID station_to_join = GB(p2, 16, 16);
    02086   bool reuse = (station_to_join != NEW_STATION);
    02087   if (!reuse) station_to_join = INVALID_STATION;
    02088   bool distant_join = (station_to_join != INVALID_STATION);
    02089   byte airport_type = GB(p1, 0, 8);
    02090   byte layout = GB(p1, 8, 8);
    02091
    02092   if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
    02093
    02094   if (airport_type >= NUM_AIRPORTS) return CMD_ERROR;
    02095
    02096   CommandCost ret = CheckIfAuthorityAllowsNewStation(tile, flags);
    02097   if (ret.Failed()) return ret;
    02098
    02099   /* Check if a valid, buildable airport was chosen for construction */
    02100   const AirportSpec *as = AirportSpec::Get(airport_type);
    02101   if (!as->IsAvailable() || layout >= as->num_table) return CMD_ERROR;
    02102
    02103   Direction rotation = as->rotation[layout];
    02104   Town *t = ClosestTownFromTile(tile, UINT_MAX);
    02105   int w = as->size_x;
    02106   int h = as->size_y;
    02107   if (rotation == DIR_E || rotation == DIR_W) Swap(w, h);
    02108
    02109   if (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread) {
    02110     return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT);
    02111   }
    02112
    02113   CommandCost cost = CheckFlatLand(TileArea(tile, w, h), flags);
    02114   if (cost.Failed()) return cost;
    02115
    02116   /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
    02117   Town *nearest = AirportGetNearestTown(as, tile);
    02118   uint newnoise_level = GetAirportNoiseLevelForTown(as, nearest->xy, tile);
    02119
    02120   /* Check if local auth would allow a new airport */
    02121   StringID authority_refuse_message = STR_NULL;
    02122
    02123   if (_settings_game.economy.station_noise_level) {
    02124     /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
    02125     if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
    02126       authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
    02127     }
    02128   } else {
    02129     uint num = 0;
    02130     const Station *st;
    02131     FOR_ALL_STATIONS(st) {
    02132       if (st->town == t && (st->facilities & FACIL_AIRPORT) && st->airport.type != AT_OILRIG) num++;
    02133     }
    02134     if (num >= 2) {
    02135       authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
    02136     }
    02137   }
    02138
    02139   if (authority_refuse_message != STR_NULL) {
    02140     SetDParam(0, t->index);
    02141     return_cmd_error(authority_refuse_message);
    02142   }
    02143
    02144   Station *st = NULL;
    02145   ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p2, 0), TileArea(tile, w, h), &st);
    02146   if (ret.Failed()) return ret;
    02147
    02148   /* Distant join */
    02149   if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join);
    02150
    02151   /* Find a deleted station close to us */
    02152   if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
    02153
    02154   if (st != NULL) {
    02155     if (st->owner != _current_company) {
    02156       return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
    02157     }
    02158
    02159     CommandCost ret = st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TEST);
    02160     if (ret.Failed()) return ret;
    02161
    02162     if (st->airport.tile != INVALID_TILE) {
    02163       return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
    02164     }
    02165   } else {
    02166     /* allocate and initialize new station */
    02167     if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
    02168
    02169     if (flags & DC_EXEC) {
    02170       st = new Station(tile);
    02171
    02172       st->town = t;
    02173       st->string_id = GenerateStationName(st, tile, !(GetAirport(airport_type)->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_HELIPORT : STATIONNAMING_AIRPORT);
    02174
    02175       if (Company::IsValidID(_current_company)) {
    02176         SetBit(st->town->have_ratings, _current_company);
    02177       }
    02178     }
    02179   }
    02180
    02181   const AirportTileTable *it = as->table[layout];
    02182   do {
    02183     cost.AddCost(_price[PR_BUILD_STATION_AIRPORT]);
    02184   } while ((++it)->ti.x != -0x80);
    02185
    02186   if (flags & DC_EXEC) {
    02187     /* Always add the noise, so there will be no need to recalculate when option toggles */
    02188     nearest->noise_reached += newnoise_level;
    02189
    02190     st->AddFacility(FACIL_AIRPORT, tile);
    02191     st->airport.type = airport_type;
    02192     st->airport.layout = layout;
    02193     st->airport.flags = 0;
    02194     st->airport.rotation = rotation;
    02195     st->airport.psa.ResetToZero();
    02196
    02197     st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
    02198
    02199     it = as->table[layout];
    02200     do {
    02201       TileIndex cur_tile = tile + ToTileIndexDiff(it->ti);
    02202       MakeAirport(cur_tile, st->owner, st->index, it->gfx, WATER_CLASS_INVALID);
    02203       SetStationTileRandomBits(cur_tile, GB(Random(), 0, 4));
    02204       st->airport.Add(cur_tile);
    02205
    02206       if (AirportTileSpec::Get(GetTranslatedAirportTileID(it->gfx))->animation.status != ANIM_STATUS_NO_ANIMATION) AddAnimatedTile(cur_tile);
    02207     } while ((++it)->ti.x != -0x80);
    02208
    02209     /* Only call the animation trigger after all tiles have been built */
    02210     it = as->table[layout];
    02211     do {
    02212       TileIndex cur_tile = tile + ToTileIndexDiff(it->ti);
    02213       AirportTileAnimationTrigger(st, cur_tile, AAT_BUILT);
    02214     } while ((++it)->ti.x != -0x80);
    02215
    02216     UpdateAirplanesOnNewStation(st);
    02217
    02218     st->UpdateVirtCoord();
    02219     UpdateStationAcceptance(st, false);
    02220     st->RecomputeIndustriesNear();
    02221     InvalidateWindowData(WC_SELECT_STATION, 0, 0);
    02222     InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
    02223     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_PLANES);
    02224
    02225     if (_settings_game.economy.station_noise_level) {
    02226       SetWindowDirty(WC_TOWN_VIEW, st->town->index);
    02227     }
    02228   }
    02229
    02230   return cost;
    02231 }
    02232
    02239 static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags)
    02240 {
    02241   Station *st = Station::GetByTile(tile);
    02242
    02243   if (_current_company != OWNER_WATER) {
    02244     CommandCost ret = CheckOwnership(st->owner);
    02245     if (ret.Failed()) return ret;
    02246   }
    02247
    02248   tile = st->airport.tile;
    02249
    02250   CommandCost cost(EXPENSES_CONSTRUCTION);
    02251
    02252   const Aircraft *a;
    02253   FOR_ALL_AIRCRAFT(a) {
    02254     if (!a->IsNormalAircraft()) continue;
    02255     if (a->targetairport == st->index && a->state != FLYING) return CMD_ERROR;
    02256   }
    02257
    02258   TILE_AREA_LOOP(tile_cur, st->airport) {
    02259     if (!st->TileBelongsToAirport(tile_cur)) continue;
    02260
    02261     CommandCost ret = EnsureNoVehicleOnGround(tile_cur);
    02262     if (ret.Failed()) return ret;
    02263
    02264     cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]);
    02265
    02266     if (flags & DC_EXEC) {
    02267       if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur, false);
    02268       DeleteAnimatedTile(tile_cur);
    02269       DoClearSquare(tile_cur);
    02270       DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur);
    02271     }
    02272   }
    02273
    02274   if (flags & DC_EXEC) {
    02275     const AirportSpec *as = st->airport.GetSpec();
    02276     for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
    02277       DeleteWindowById(
    02278         WC_VEHICLE_DEPOT, st->airport.GetHangarTile(i)
    02279       );
    02280     }
    02281
    02282     /* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
    02283      * And as for construction, always remove it, even if the setting is not set, in order to avoid the
    02284      * need of recalculation */
    02285     Town *nearest = AirportGetNearestTown(as, tile);
    02286     nearest->noise_reached -= GetAirportNoiseLevelForTown(as, nearest->xy, tile);
    02287
    02288     st->rect.AfterRemoveRect(st, st->airport);
    02289
    02290     st->airport.Clear();
    02291     st->facilities &= ~FACIL_AIRPORT;
    02292     st->airport.psa.ResetToZero();
    02293
    02294     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_PLANES);
    02295
    02296     if (_settings_game.economy.station_noise_level) {
    02297       SetWindowDirty(WC_TOWN_VIEW, st->town->index);
    02298     }
    02299
    02300     st->UpdateVirtCoord();
    02301     st->RecomputeIndustriesNear();
    02302     DeleteStationIfEmpty(st);
    02303     DeleteNewGRFInspectWindow(GSF_AIRPORTS, st->index);
    02304   }
    02305
    02306   return cost;
    02307 }
    02308
    02315 bool HasStationInUse(StationID station, bool include_company, CompanyID company)
    02316 {
    02317   const Vehicle *v;
    02318   FOR_ALL_VEHICLES(v) {
    02319     if ((v->owner == company) == include_company) {
    02320       const Order *order;
    02321       FOR_VEHICLE_ORDERS(v, order) {
    02322         if ((order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT)) && order->GetDestination() == station) {
    02323           return true;
    02324         }
    02325       }
    02326     }
    02327   }
    02328   return false;
    02329 }
    02330
    02331 static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
    02332   {-1,  0},
    02333   { 0,  0},
    02334   { 0,  0},
    02335   { 0, -1}
    02336 };
    02337 static const byte _dock_w_chk[4] = { 2, 1, 2, 1 };
    02338 static const byte _dock_h_chk[4] = { 1, 2, 1, 2 };
    02339
    02349 CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    02350 {
    02351   StationID station_to_join = GB(p2, 16, 16);
    02352   bool reuse = (station_to_join != NEW_STATION);
    02353   if (!reuse) station_to_join = INVALID_STATION;
    02354   bool distant_join = (station_to_join != INVALID_STATION);
    02355
    02356   if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
    02357
    02358   DiagDirection direction = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
    02359   if (direction == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
    02360   direction = ReverseDiagDir(direction);
    02361
    02362   /* Docks cannot be placed on rapids */
    02363   if (HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
    02364
    02365   CommandCost ret = CheckIfAuthorityAllowsNewStation(tile, flags);
    02366   if (ret.Failed()) return ret;
    02367
    02368   if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
    02369
    02370   ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    02371   if (ret.Failed()) return ret;
    02372
    02373   TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
    02374
    02375   if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
    02376     return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
    02377   }
    02378
    02379   if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
    02380
    02381   /* Get the water class of the water tile before it is cleared.*/
    02382   WaterClass wc = GetWaterClass(tile_cur);
    02383
    02384   ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    02385   if (ret.Failed()) return ret;
    02386
    02387   tile_cur += TileOffsByDiagDir(direction);
    02388   if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
    02389     return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
    02390   }
    02391
    02392   /* middle */
    02393   Station *st = NULL;
    02394   ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p1, 0),
    02395       TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
    02396           _dock_w_chk[direction], _dock_h_chk[direction]), &st);
    02397   if (ret.Failed()) return ret;
    02398
    02399   /* Distant join */
    02400   if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join);
    02401
    02402   /* Find a deleted station close to us */
    02403   if (st == NULL && reuse) st = GetClosestDeletedStation(tile);
    02404
    02405   if (st != NULL) {
    02406     if (st->owner != _current_company) {
    02407       return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION);
    02408     }
    02409
    02410     CommandCost ret = st->rect.BeforeAddRect(
    02411         tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
    02412         _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TEST);
    02413     if (ret.Failed()) return ret;
    02414
    02415     if (st->dock_tile != INVALID_TILE) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_DOCK);
    02416   } else {
    02417     /* allocate and initialize new station */
    02418     if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
    02419
    02420     if (flags & DC_EXEC) {
    02421       st = new Station(tile);
    02422
    02423       st->town = ClosestTownFromTile(tile, UINT_MAX);
    02424       st->string_id = GenerateStationName(st, tile, STATIONNAMING_DOCK);
    02425
    02426       if (Company::IsValidID(_current_company)) {
    02427         SetBit(st->town->have_ratings, _current_company);
    02428       }
    02429     }
    02430   }
    02431
    02432   if (flags & DC_EXEC) {
    02433     st->dock_tile = tile;
    02434     st->AddFacility(FACIL_DOCK, tile);
    02435
    02436     st->rect.BeforeAddRect(
    02437         tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
    02438         _dock_w_chk[direction], _dock_h_chk[direction], StationRect::ADD_TRY);
    02439
    02440     MakeDock(tile, st->owner, st->index, direction, wc);
    02441
    02442     st->UpdateVirtCoord();
    02443     UpdateStationAcceptance(st, false);
    02444     st->RecomputeIndustriesNear();
    02445     InvalidateWindowData(WC_SELECT_STATION, 0, 0);
    02446     InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
    02447     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_SHIPS);
    02448   }
    02449
    02450   return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
    02451 }
    02452
    02459 static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags)
    02460 {
    02461   Station *st = Station::GetByTile(tile);
    02462   CommandCost ret = CheckOwnership(st->owner);
    02463   if (ret.Failed()) return ret;
    02464
    02465   TileIndex tile1 = st->dock_tile;
    02466   TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
    02467
    02468   ret = EnsureNoVehicleOnGround(tile1);
    02469   if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
    02470   if (ret.Failed()) return ret;
    02471
    02472   if (flags & DC_EXEC) {
    02473     DoClearSquare(tile1);
    02474     MarkTileDirtyByTile(tile1);
    02475     MakeWaterKeepingClass(tile2, st->owner);
    02476
    02477     st->rect.AfterRemoveTile(st, tile1);
    02478     st->rect.AfterRemoveTile(st, tile2);
    02479
    02480     st->dock_tile = INVALID_TILE;
    02481     st->facilities &= ~FACIL_DOCK;
    02482
    02483     SetWindowWidgetDirty(WC_STATION_VIEW, st->index, SVW_SHIPS);
    02484     st->UpdateVirtCoord();
    02485     st->RecomputeIndustriesNear();
    02486     DeleteStationIfEmpty(st);
    02487   }
    02488
    02489   return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]);
    02490 }
    02491
    02492 #include "table/station_land.h"
    02493
    02494 const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx)
    02495 {
    02496   return &_station_display_datas[st][gfx];
    02497 }
    02498
    02499 static void DrawTile_Station(TileInfo *ti)
    02500 {
    02501   const NewGRFSpriteLayout *layout = NULL;
    02502   DrawTileSprites tmp_rail_layout;
    02503   const DrawTileSprites *t = NULL;
    02504   RoadTypes roadtypes;
    02505   int32 total_offset;
    02506   const RailtypeInfo *rti = NULL;
    02507   uint32 relocation = 0;
    02508   uint32 ground_relocation = 0;
    02509   const BaseStation *st = NULL;
    02510   const StationSpec *statspec = NULL;
    02511   uint tile_layout = 0;
    02512
    02513   if (HasStationRail(ti->tile)) {
    02514     rti = GetRailTypeInfo(GetRailType(ti->tile));
    02515     roadtypes = ROADTYPES_NONE;
    02516     total_offset = rti->GetRailtypeSpriteOffset();
    02517
    02518     if (IsCustomStationSpecIndex(ti->tile)) {
    02519       /* look for customization */
    02520       st = BaseStation::GetByTile(ti->tile);
    02521       statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
    02522
    02523       if (statspec != NULL) {
    02524         tile_layout = GetStationGfx(ti->tile);
    02525
    02526         if (HasBit(statspec->callback_mask, CBM_STATION_SPRITE_LAYOUT)) {
    02527           uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile);
    02528           if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + GetRailStationAxis(ti->tile);
    02529         }
    02530
    02531         /* Ensure the chosen tile layout is valid for this custom station */
    02532         if (statspec->renderdata != NULL) {
    02533           layout = &statspec->renderdata[tile_layout < statspec->tiles ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
    02534           if (!layout->NeedsPreprocessing()) {
    02535             t = layout;
    02536             layout = NULL;
    02537           }
    02538         }
    02539       }
    02540     }
    02541   } else {
    02542     roadtypes = IsRoadStop(ti->tile) ? GetRoadTypes(ti->tile) : ROADTYPES_NONE;
    02543     total_offset = 0;
    02544   }
    02545
    02546   if (IsAirport(ti->tile)) {
    02547     StationGfx gfx = GetAirportGfx(ti->tile);
    02548     if (gfx >= NEW_AIRPORTTILE_OFFSET) {
    02549       const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
    02550       if (ats->grf_prop.spritegroup[0] != NULL && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), gfx, ats)) {
    02551         return;
    02552       }
    02553       /* No sprite group (or no valid one) found, meaning no graphics associated.
    02554        * Use the substitute one instead */
    02555       assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
    02556       gfx = ats->grf_prop.subst_id;
    02557     }
    02558     switch (gfx) {
    02559       case APT_RADAR_GRASS_FENCE_SW:
    02560         t = &_station_display_datas_airport_radar_grass_fence_sw[GetAnimationFrame(ti->tile)];
    02561         break;
    02562       case APT_GRASS_FENCE_NE_FLAG:
    02563         t = &_station_display_datas_airport_flag_grass_fence_ne[GetAnimationFrame(ti->tile)];
    02564         break;
    02565       case APT_RADAR_FENCE_SW:
    02566         t = &_station_display_datas_airport_radar_fence_sw[GetAnimationFrame(ti->tile)];
    02567         break;
    02568       case APT_RADAR_FENCE_NE:
    02569         t = &_station_display_datas_airport_radar_fence_ne[GetAnimationFrame(ti->tile)];
    02570         break;
    02571       case APT_GRASS_FENCE_NE_FLAG_2:
    02572         t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetAnimationFrame(ti->tile)];
    02573         break;
    02574     }
    02575   }
    02576
    02577   Owner owner = GetTileOwner(ti->tile);
    02578
    02579   PaletteID palette;
    02580   if (Company::IsValidID(owner)) {
    02581     palette = COMPANY_SPRITE_COLOUR(owner);
    02582   } else {
    02583     /* Some stations are not owner by a company, namely oil rigs */
    02584     palette = PALETTE_TO_GREY;
    02585   }
    02586
    02587   if (layout == NULL && (t == NULL || t->seq == NULL)) t = GetStationTileLayout(GetStationType(ti->tile), GetStationGfx(ti->tile));
    02588
    02589   /* don't show foundation for docks */
    02590   if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
    02591     if (statspec != NULL && HasBit(statspec->flags, SSF_CUSTOM_FOUNDATIONS)) {
    02592       /* Station has custom foundations.
    02593        * Check whether the foundation continues beyond the tile's upper sides. */
    02594       uint edge_info = 0;
    02595       uint z;
    02596       Slope slope = GetFoundationSlope(ti->tile, &z);
    02597       if (!HasFoundationNW(ti->tile, slope, z)) SetBit(edge_info, 0);
    02598       if (!HasFoundationNE(ti->tile, slope, z)) SetBit(edge_info, 1);
    02599       SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile, tile_layout, edge_info);
    02600
    02601       if (HasBit(statspec->flags, SSF_EXTENDED_FOUNDATIONS)) {
    02602         /* Station provides extended foundations. */
    02603
    02604         static const uint8 foundation_parts[] = {
    02605           0, 0, 0, 0, // Invalid,  Invalid,   Invalid,   SLOPE_SW
    02606           0, 1, 2, 3, // Invalid,  SLOPE_EW,  SLOPE_SE,  SLOPE_WSE
    02607           0, 4, 5, 6, // Invalid,  SLOPE_NW,  SLOPE_NS,  SLOPE_NWS
    02608           7, 8, 9     // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
    02609         };
    02610
    02611         AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
    02612       } else {
    02613         /* Draw simple foundations, built up from 8 possible foundation sprites. */
    02614
    02615         /* Each set bit represents one of the eight composite sprites to be drawn.
    02616          * 'Invalid' entries will not drawn but are included for completeness. */
    02617         static const uint8 composite_foundation_parts[] = {
    02618           /* Invalid  (00000000), Invalid   (11010001), Invalid   (11100100), SLOPE_SW  (11100000) */
    02619              0x00,                0xD1,                 0xE4,                 0xE0,
    02620           /* Invalid  (11001010), SLOPE_EW  (11001001), SLOPE_SE  (11000100), SLOPE_WSE (11000000) */
    02621              0xCA,                0xC9,                 0xC4,                 0xC0,
    02622           /* Invalid  (11010010), SLOPE_NW  (10010001), SLOPE_NS  (11100100), SLOPE_NWS (10100000) */
    02623              0xD2,                0x91,                 0xE4,                 0xA0,
    02624           /* SLOPE_NE (01001010), SLOPE_ENW (00001001), SLOPE_SEN (01000100) */
    02625              0x4A,                0x09,                 0x44
    02626         };
    02627
    02628         uint8 parts = composite_foundation_parts[ti->tileh];
    02629
    02630         /* If foundations continue beyond the tile's upper sides then
    02631          * mask out the last two pieces. */
    02632         if (HasBit(edge_info, 0)) ClrBit(parts, 6);
    02633         if (HasBit(edge_info, 1)) ClrBit(parts, 7);
    02634
    02635         if (parts == 0) {
    02636           /* We always have to draw at least one sprite to make sure there is a boundingbox and a sprite with the
    02637            * correct offset for the childsprites.
    02638            * So, draw the (completely empty) sprite of the default foundations. */
    02639           goto draw_default_foundation;
    02640         }
    02641
    02642         StartSpriteCombine();
    02643         for (int i = 0; i < 8; i++) {
    02644           if (HasBit(parts, i)) {
    02645             AddSortableSpriteToDraw(image + i, PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
    02646           }
    02647         }
    02648         EndSpriteCombine();
    02649       }
    02650
    02651       OffsetGroundSprite(31, 1);
    02652       ti->z += ApplyFoundationToSlope(FOUNDATION_LEVELED, &ti->tileh);
    02653     } else {
    02654 draw_default_foundation:
    02655       DrawFoundation(ti, FOUNDATION_LEVELED);
    02656     }
    02657   }
    02658
    02659   if (IsBuoy(ti->tile) || IsDock(ti->tile) || (IsOilRig(ti->tile) && IsTileOnWater(ti->tile))) {
    02660     if (ti->tileh == SLOPE_FLAT) {
    02661       DrawWaterClassGround(ti);
    02662     } else {
    02663       assert(IsDock(ti->tile));
    02664       TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
    02665       WaterClass wc = GetWaterClass(water_tile);
    02666       if (wc == WATER_CLASS_SEA) {
    02667         DrawShoreTile(ti->tileh);
    02668       } else {
    02669         DrawClearLandTile(ti, 3);
    02670       }
    02671     }
    02672   } else {
    02673     if (layout != NULL) {
    02674       /* Sprite layout which needs preprocessing */
    02675       bool separate_ground = HasBit(statspec->flags, SSF_SEPARATE_GROUND);
    02676       uint32 var10_values = layout->PrepareLayout(total_offset, rti->fallback_railtype, 0, separate_ground);
    02677       uint8 var10;
    02678       FOR_EACH_SET_BIT(var10, var10_values) {
    02679         uint32 var10_relocation = GetCustomStationRelocation(statspec, st, ti->tile, var10);
    02680         layout->ProcessRegisters(var10, var10_relocation, separate_ground);
    02681       }
    02682       tmp_rail_layout.seq = layout->GetLayout(&tmp_rail_layout.ground);
    02683       t = &tmp_rail_layout;
    02684       total_offset = 0;
    02685     } else if (statspec != NULL) {
    02686       /* Simple sprite layout */
    02687       ground_relocation = relocation = GetCustomStationRelocation(statspec, st, ti->tile, 0);
    02688       if (HasBit(statspec->flags, SSF_SEPARATE_GROUND)) {
    02689         ground_relocation = GetCustomStationRelocation(statspec, st, ti->tile, 1);
    02690       }
    02691       ground_relocation += rti->fallback_railtype;
    02692     }
    02693
    02694     SpriteID image = t->ground.sprite;
    02695     PaletteID pal  = t->ground.pal;
    02696     if (rti != NULL && rti->UsesOverlay() && (image == SPR_RAIL_TRACK_X || image == SPR_RAIL_TRACK_Y)) {
    02697       SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
    02698       DrawGroundSprite(SPR_FLAT_GRASS_TILE, PAL_NONE);
    02699       DrawGroundSprite(ground + (image == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PAL_NONE);
    02700
    02701       if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
    02702         SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
    02703         DrawGroundSprite(overlay + (image == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PALETTE_CRASH);
    02704       }
    02705     } else {
    02706       image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
    02707       if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
    02708       DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
    02709
    02710       /* PBS debugging, draw reserved tracks darker */
    02711       if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
    02712         const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
    02713         DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
    02714       }
    02715     }
    02716   }
    02717
    02718   if (HasStationRail(ti->tile) && HasCatenaryDrawn(GetRailType(ti->tile)) && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
    02719
    02720   if (HasBit(roadtypes, ROADTYPE_TRAM)) {
    02721     Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
    02722     DrawGroundSprite((HasBit(roadtypes, ROADTYPE_ROAD) ? SPR_TRAMWAY_OVERLAY : SPR_TRAMWAY_TRAM) + (axis ^ 1), PAL_NONE);
    02723     DrawTramCatenary(ti, axis == AXIS_X ? ROAD_X : ROAD_Y);
    02724   }
    02725
    02726   if (IsRailWaypoint(ti->tile)) {
    02727     /* Don't offset the waypoint graphics; they're always the same. */
    02728     total_offset = 0;
    02729   }
    02730
    02731   DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
    02732 }
    02733
    02734 void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
    02735 {
    02736   int32 total_offset = 0;
    02737   PaletteID pal = COMPANY_SPRITE_COLOUR(_local_company);
    02738   const DrawTileSprites *t = GetStationTileLayout(st, image);
    02739   const RailtypeInfo *rti = NULL;
    02740
    02741   if (railtype != INVALID_RAILTYPE) {
    02742     rti = GetRailTypeInfo(railtype);
    02743     total_offset = rti->GetRailtypeSpriteOffset();
    02744   }
    02745
    02746   SpriteID img = t->ground.sprite;
    02747   if ((img == SPR_RAIL_TRACK_X || img == SPR_RAIL_TRACK_Y) && rti->UsesOverlay()) {
    02748     SpriteID ground = GetCustomRailSprite(rti, INVALID_TILE, RTSG_GROUND);
    02749     DrawSprite(SPR_FLAT_GRASS_TILE, PAL_NONE, x, y);
    02750     DrawSprite(ground + (img == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PAL_NONE, x, y);
    02751   } else {
    02752     DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
    02753   }
    02754
    02755   if (roadtype == ROADTYPE_TRAM) {
    02756     DrawSprite(SPR_TRAMWAY_TRAM + (t->ground.sprite == SPR_ROAD_PAVED_STRAIGHT_X ? 1 : 0), PAL_NONE, x, y);
    02757   }
    02758
    02759   /* Default waypoint has no railtype specific sprites */
    02760   DrawRailTileSeqInGUI(x, y, t, st == STATION_WAYPOINT ? 0 : total_offset, 0, pal);
    02761 }
    02762
    02763 static uint GetSlopeZ_Station(TileIndex tile, uint x, uint y)
    02764 {
    02765   return GetTileMaxZ(tile);
    02766 }
    02767
    02768 static Foundation GetFoundation_Station(TileIndex tile, Slope tileh)
    02769 {
    02770   return FlatteningFoundation(tileh);
    02771 }
    02772
    02773 static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
    02774 {
    02775   td->owner[0] = GetTileOwner(tile);
    02776   if (IsDriveThroughStopTile(tile)) {
    02777     Owner road_owner = INVALID_OWNER;
    02778     Owner tram_owner = INVALID_OWNER;
    02779     RoadTypes rts = GetRoadTypes(tile);
    02780     if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
    02781     if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
    02782
    02783     /* Is there a mix of owners? */
    02784     if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) ||
    02785         (road_owner != INVALID_OWNER && road_owner != td->owner[0])) {
    02786       uint i = 1;
    02787       if (road_owner != INVALID_OWNER) {
    02788         td->owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
    02789         td->owner[i] = road_owner;
    02790         i++;
    02791       }
    02792       if (tram_owner != INVALID_OWNER) {
    02793         td->owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
    02794         td->owner[i] = tram_owner;
    02795       }
    02796     }
    02797   }
    02798   td->build_date = BaseStation::GetByTile(tile)->build_date;
    02799
    02800   if (HasStationTileRail(tile)) {
    02801     const StationSpec *spec = GetStationSpec(tile);
    02802
    02803     if (spec != NULL) {
    02804       td->station_class = StationClass::GetName(spec->cls_id);
    02805       td->station_name  = spec->name;
    02806
    02807       if (spec->grf_prop.grffile != NULL) {
    02808         const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grffile->grfid);
    02809         td->grf = gc->GetName();
    02810       }
    02811     }
    02812
    02813     const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
    02814     td->rail_speed = rti->max_speed;
    02815   }
    02816
    02817   if (IsAirport(tile)) {
    02818     const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
    02819     td->airport_class = AirportClass::GetName(as->cls_id);
    02820     td->airport_name = as->name;
    02821
    02822     const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile);
    02823     td->airport_tile_name = ats->name;
    02824
    02825     if (as->grf_prop.grffile != NULL) {
    02826       const GRFConfig *gc = GetGRFConfig(as->grf_prop.grffile->grfid);
    02827       td->grf = gc->GetName();
    02828     } else if (ats->grf_prop.grffile != NULL) {
    02829       const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grffile->grfid);
    02830       td->grf = gc->GetName();
    02831     }
    02832   }
    02833
    02834   StringID str;
    02835   switch (GetStationType(tile)) {
    02836     default: NOT_REACHED();
    02837     case STATION_RAIL:     str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
    02838     case STATION_AIRPORT:
    02839       str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
    02840       break;
    02841     case STATION_TRUCK:    str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
    02842     case STATION_BUS:      str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
    02843     case STATION_OILRIG:   str = STR_INDUSTRY_NAME_OIL_RIG; break;
    02844     case STATION_DOCK:     str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
    02845     case STATION_BUOY:     str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
    02846     case STATION_WAYPOINT: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
    02847   }
    02848   td->str = str;
    02849 }
    02850
    02851
    02852 static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
    02853 {
    02854   TrackBits trackbits = TRACK_BIT_NONE;
    02855
    02856   switch (mode) {
    02857     case TRANSPORT_RAIL:
    02858       if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
    02859         trackbits = TrackToTrackBits(GetRailStationTrack(tile));
    02860       }
    02861       break;
    02862
    02863     case TRANSPORT_WATER:
    02864       /* buoy is coded as a station, it is always on open water */
    02865       if (IsBuoy(tile)) {
    02866         trackbits = TRACK_BIT_ALL;
    02867         /* remove tracks that connect NE map edge */
    02868         if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
    02869         /* remove tracks that connect NW map edge */
    02870         if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
    02871       }
    02872       break;
    02873
    02874     case TRANSPORT_ROAD:
    02875       if ((GetRoadTypes(tile) & sub_mode) != 0 && IsRoadStop(tile)) {
    02876         DiagDirection dir = GetRoadStopDir(tile);
    02877         Axis axis = DiagDirToAxis(dir);
    02878
    02879         if (side != INVALID_DIAGDIR) {
    02880           if (axis != DiagDirToAxis(side) || (IsStandardRoadStopTile(tile) && dir != side)) break;
    02881         }
    02882
    02883         trackbits = AxisToTrackBits(axis);
    02884       }
    02885       break;
    02886
    02887     default:
    02888       break;
    02889   }
    02890
    02891   return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), TRACKDIR_BIT_NONE);
    02892 }
    02893
    02894
    02895 static void TileLoop_Station(TileIndex tile)
    02896 {
    02897   /* FIXME -- GetTileTrackStatus_Station -> animated stationtiles
    02898    * hardcoded.....not good */
    02899   switch (GetStationType(tile)) {
    02900     case STATION_AIRPORT:
    02901       AirportTileAnimationTrigger(Station::GetByTile(tile), tile, AAT_TILELOOP);
    02902       break;
    02903
    02904     case STATION_DOCK:
    02905       if (GetTileSlope(tile, NULL) != SLOPE_FLAT) break; // only handle water part
    02906       /* FALL THROUGH */
    02907     case STATION_OILRIG: //(station part)
    02908     case STATION_BUOY:
    02909       TileLoop_Water(tile);
    02910       break;
    02911
    02912     default: break;
    02913   }
    02914 }
    02915
    02916
    02917 static void AnimateTile_Station(TileIndex tile)
    02918 {
    02919   if (HasStationRail(tile)) {
    02920     AnimateStationTile(tile);
    02921     return;
    02922   }
    02923
    02924   if (IsAirport(tile)) {
    02925     AnimateAirportTile(tile);
    02926   }
    02927 }
    02928
    02929
    02930 static bool ClickTile_Station(TileIndex tile)
    02931 {
    02932   const BaseStation *bst = BaseStation::GetByTile(tile);
    02933
    02934   if (bst->facilities & FACIL_WAYPOINT) {
    02935     ShowWaypointWindow(Waypoint::From(bst));
    02936   } else if (IsHangar(tile)) {
    02937     const Station *st = Station::From(bst);
    02938     ShowDepotWindow(st->airport.GetHangarTile(st->airport.GetHangarNum(tile)), VEH_AIRCRAFT);
    02939   } else {
    02940     ShowStationViewWindow(bst->index);
    02941   }
    02942   return true;
    02943 }
    02944
    02945 static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
    02946 {
    02947   if (v->type == VEH_TRAIN) {
    02948     StationID station_id = GetStationIndex(tile);
    02949     if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
    02950     if (!IsRailStation(tile) || !v->IsFrontEngine()) return VETSB_CONTINUE;
    02951
    02952     int station_ahead;
    02953     int station_length;
    02954     int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
    02955
    02956     /* Stop whenever that amount of station ahead + the distance from the
    02957      * begin of the platform to the stop location is longer than the length
    02958      * of the platform. Station ahead 'includes' the current tile where the
    02959      * vehicle is on, so we need to substract that. */
    02960     if (!IsInsideBS(stop + station_ahead, station_length, TILE_SIZE)) return VETSB_CONTINUE;
    02961
    02962     DiagDirection dir = DirToDiagDir(v->direction);
    02963
    02964     x &= 0xF;
    02965     y &= 0xF;
    02966
    02967     if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
    02968     if (y == TILE_SIZE / 2) {
    02969       if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
    02970       stop &= TILE_SIZE - 1;
    02971
    02972       if (x == stop) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station
    02973       if (x < stop) {
    02974         uint16 spd;
    02975
    02976         v->vehstatus |= VS_TRAIN_SLOWING;
    02977         spd = max(0, (stop - x) * 20 - 15);
    02978         if (spd < v->cur_speed) v->cur_speed = spd;
    02979       }
    02980     }
    02981   } else if (v->type == VEH_ROAD) {
    02982     RoadVehicle *rv = RoadVehicle::From(v);
    02983     if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
    02984       if (IsRoadStop(tile) && rv->IsFrontEngine()) {
    02985         /* Attempt to allocate a parking bay in a road stop */
    02986         return RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv) ? VETSB_CONTINUE : VETSB_CANNOT_ENTER;
    02987       }
    02988     }
    02989   }
    02990
    02991   return VETSB_CONTINUE;
    02992 }
    02993
    03000 static bool StationHandleBigTick(BaseStation *st)
    03001 {
    03002   if (!st->IsInUse() && ++st->delete_ctr >= 8) {
    03003     delete st;
    03004     return false;
    03005   }
    03006
    03007   if (Station::IsExpected(st)) {
    03008     for (CargoID i = 0; i < NUM_CARGO; i++) {
    03009       ClrBit(Station::From(st)->goods[i].acceptance_pickup, GoodsEntry::GES_ACCEPTED_BIGTICK);
    03010     }
    03011   }
    03012
    03013
    03014   if ((st->facilities & FACIL_WAYPOINT) == 0) UpdateStationAcceptance(Station::From(st), true);
    03015
    03016   return true;
    03017 }
    03018
    03019 static inline void byte_inc_sat(byte *p)
    03020 {
    03021   byte b = *p + 1;
    03022   if (b != 0) *p = b;
    03023 }
    03024
    03025 static void UpdateStationRating(Station *st)
    03026 {
    03027   bool waiting_changed = false;
    03028
    03029   byte_inc_sat(&st->time_since_load);
    03030   byte_inc_sat(&st->time_since_unload);
    03031
    03032   const CargoSpec *cs;
    03033   FOR_ALL_CARGOSPECS(cs) {
    03034     GoodsEntry *ge = &st->goods[cs->Index()];
    03035     /* Slowly increase the rating back to his original level in the case we
    03036      *  didn't deliver cargo yet to this station. This happens when a bribe
    03037      *  failed while you didn't moved that cargo yet to a station. */
    03038     if (!HasBit(ge->acceptance_pickup, GoodsEntry::GES_PICKUP) && ge->rating < INITIAL_STATION_RATING) {
    03039       ge->rating++;
    03040     }
    03041
    03042     /* Only change the rating if we are moving this cargo */
    03043     if (HasBit(ge->acceptance_pickup, GoodsEntry::GES_PICKUP)) {
    03044       byte_inc_sat(&ge->days_since_pickup);
    03045
    03046       bool skip = false;
    03047       int rating = 0;
    03048       uint waiting = ge->cargo.Count();
    03049
    03050       /* num_dests is at least 1 if there is any cargo as
    03051        * INVALID_STATION is also a destination.
    03052        */
    03053       uint num_dests = (uint)ge->cargo.Packets()->MapSize();
    03054
    03055       /* Average amount of cargo per next hop, but prefer solitary stations
    03056        * with only one or two next hops. They are allowed to have more
    03057        * cargo waiting per next hop.
    03058        * With manual cargo distribution waiting_avg = waiting / 2 as then
    03059        * INVALID_STATION is the only destination.
    03060        */
    03061       uint waiting_avg = waiting / (num_dests + 1);
    03062
    03063       if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) {
    03064         /* Perform custom station rating. If it succeeds the speed, days in transit and
    03065          * waiting cargo ratings must not be executed. */
    03066
    03067         /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
    03068         uint last_speed = ge->last_speed;
    03069         if (last_speed == 0) last_speed = 0xFF;
    03070
    03071         uint32 var18 = min(ge->days_since_pickup, 0xFF) | (min(ge->max_waiting_cargo, 0xFFFF) << 8) | (min(last_speed, 0xFF) << 24);
    03072         /* Convert to the 'old' vehicle types */
    03073         uint32 var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
    03074         uint16 callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
    03075         if (callback != CALLBACK_FAILED) {
    03076           skip = true;
    03077           rating = GB(callback, 0, 14);
    03078
    03079           /* Simulate a 15 bit signed value */
    03080           if (HasBit(callback, 14)) rating -= 0x4000;
    03081         }
    03082       }
    03083
    03084       if (!skip) {
    03085         int b = ge->last_speed - 85;
    03086         if (b >= 0) rating += b >> 2;
    03087
    03088         byte days = ge->days_since_pickup;
    03089         if (st->last_vehicle_type == VEH_SHIP) days >>= 2;
    03090         (days > 21) ||
    03091         (rating += 25, days > 12) ||
    03092         (rating += 25, days > 6) ||
    03093         (rating += 45, days > 3) ||
    03094         (rating += 35, true);
    03095
    03096         (rating -= 90, ge->max_waiting_cargo > 1500) ||
    03097         (rating += 55, ge->max_waiting_cargo > 1000) ||
    03098         (rating += 35, ge->max_waiting_cargo > 600) ||
    03099         (rating += 10, ge->max_waiting_cargo > 300) ||
    03100         (rating += 20, ge->max_waiting_cargo > 100) ||
    03101         (rating += 10, true);
    03102       }
    03103
    03104       if (Company::IsValidID(st->owner) && HasBit(st->town->statues, st->owner)) rating += 26;
    03105
    03106       byte age = ge->last_age;
    03107       (age >= 3) ||
    03108       (rating += 10, age >= 2) ||
    03109       (rating += 10, age >= 1) ||
    03110       (rating += 13, true);
    03111
    03112       {
    03113         int or_ = ge->rating; // old rating
    03114
    03115         /* only modify rating in steps of -2, -1, 0, 1 or 2 */
    03116         ge->rating = rating = or_ + Clamp(Clamp(rating, 0, 255) - or_, -2, 2);
    03117
    03118         /* if rating is <= 64 and more than 100 items waiting on average per destination,
    03119          * remove some random amount of goods from the station */
    03120         if (rating <= 64 && waiting_avg >= 100) {
    03121           int dec = Random() & 0x1F;
    03122           if (waiting_avg < 200) dec &= 7;
    03123           waiting -= (dec + 1) * num_dests;
    03124           waiting_changed = true;
    03125         }
    03126
    03127         /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
    03128         if (rating <= 127 && waiting != 0) {
    03129           uint32 r = Random();
    03130           if (rating <= (int)GB(r, 0, 7)) {
    03131             /* Need to have int, otherwise it will just overflow etc. */
    03132             waiting = max((int)waiting - (int)((GB(r, 8, 2) - 1) * num_dests), 0);
    03133             waiting_changed = true;
    03134           }
    03135         }
    03136
    03137         /* At some point we really must cap the cargo. Previously this
    03138          * was a strict 4095, but now we'll have a less strict, but
    03139          * increasingly agressive truncation of the amount of cargo. */
    03140         static const uint WAITING_CARGO_THRESHOLD  = 1 << 12;
    03141         static const uint WAITING_CARGO_CUT_FACTOR = 1 <<  6;
    03142         static const uint MAX_WAITING_CARGO        = 1 << 15;
    03143
    03144         if (waiting > WAITING_CARGO_THRESHOLD) {
    03145           uint difference = waiting - WAITING_CARGO_THRESHOLD;
    03146           waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
    03147
    03148           waiting = min(waiting, MAX_WAITING_CARGO);
    03149           waiting_changed = true;
    03150         }
    03151
    03152         if (waiting_changed) {
    03153           /* feed back the exact own waiting cargo at this station for the
    03154            * next rating calculation.
    03155            */
    03156           ge->max_waiting_cargo = 0;
    03157
    03158           /* If truncating also punish the source stations' ratings to
    03159            * decrease the flow of incoming cargo. */
    03160
    03161           StationCargoAmountMap waiting_per_source;
    03162           ge->cargo.CountAndTruncate(waiting, waiting_per_source);
    03163           for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) {
    03164             Station *source_station = Station::GetIfValid(i->first);
    03165             if (source_station == NULL) continue;
    03166
    03167             GoodsEntry &source_ge = source_station->goods[cs->Index()];
    03168             source_ge.max_waiting_cargo = max(source_ge.max_waiting_cargo, i->second);
    03169           }
    03170         } else {
    03171           /* if the average number per next hop is low, be more forgiving. */
    03172           ge->max_waiting_cargo = waiting_avg;
    03173         }
    03174       }
    03175     }
    03176   }
    03177
    03178   StationID index = st->index;
    03179   if (waiting_changed) {
    03180     SetWindowDirty(WC_STATION_VIEW, index); // update whole window
    03181   } else {
    03182     SetWindowWidgetDirty(WC_STATION_VIEW, index, SVW_RATINGLIST); // update only ratings list
    03183   }
    03184 }
    03185
    03192 void DeleteStaleFlows(StationID at, CargoID c_id, StationID to)
    03193 {
    03194   FlowStatMap &flows = Station::Get(at)->goods[c_id].flows;
    03195   for (FlowStatMap::iterator f_it = flows.begin(); f_it != flows.end();) {
    03196     FlowStatSet &s_flows = f_it->second;
    03197     for (FlowStatSet::iterator s_it = s_flows.begin(); s_it != s_flows.end();) {
    03198       if (s_it->Via() == to) {
    03199         s_flows.erase(s_it++);
    03200         break; // There can only be one flow stat for this remote station in each set.
    03201       } else {
    03202         ++s_it;
    03203       }
    03204     }
    03205     if (s_flows.empty()) {
    03206       flows.erase(f_it++);
    03207     } else {
    03208       ++f_it;
    03209     }
    03210   }
    03211 }
    03212
    03219 uint GetMovingAverageLength(const Station *from, const Station *to)
    03220 {
    03221   return LinkStat::MIN_AVERAGE_LENGTH + (DistanceManhattan(from->xy, to->xy) >> 2);
    03222 }
    03223
    03227 void Station::RunAverages()
    03228 {
    03229   FlowStatSet new_flows;
    03230   for (int goods_index = 0; goods_index < NUM_CARGO; ++goods_index) {
    03231     LinkStatMap &links = this->goods[goods_index].link_stats;
    03232     for (LinkStatMap::iterator i = links.begin(); i != links.end();) {
    03233       StationID id = i->first;
    03234       Station *other = Station::GetIfValid(id);
    03235       if (other == NULL) {
    03236         this->goods[goods_index].cargo.RerouteStalePackets(id);
    03237         links.erase(i++);
    03238       } else {
    03239         LinkStat &ls = i->second;
    03240         ls.Decrease();
    03241         if (ls.IsNull()) {
    03242           DeleteStaleFlows(this->index, goods_index, id);
    03243           this->goods[goods_index].cargo.RerouteStalePackets(id);
    03244           links.erase(i++);
    03245         } else {
    03246           ++i;
    03247         }
    03248       }
    03249     }
    03250
    03251     if (_settings_game.linkgraph.GetDistributionType(goods_index) == DT_MANUAL) {
    03252       this->goods[goods_index].flows.clear();
    03253       continue;
    03254     }
    03255
    03256     FlowStatMap &flows = this->goods[goods_index].flows;
    03257     for (FlowStatMap::iterator i = flows.begin(); i != flows.end();) {
    03258       if (!Station::IsValidID(i->first)) {
    03259         flows.erase(i++);
    03260       } else {
    03261         FlowStatSet &flow_set = i->second;
    03262         for (FlowStatSet::iterator j = flow_set.begin(); j != flow_set.end(); ++j) {
    03263           if (Station::IsValidID(j->Via())) {
    03264             new_flows.insert(j->GetDecreasedCopy());
    03265           }
    03266         }
    03267         flow_set.swap(new_flows);
    03268         new_flows.clear();
    03269         ++i;
    03270       }
    03271     }
    03272   }
    03273 }
    03274
    03280 void RecalcFrozenIfLoading(const Vehicle *v)
    03281 {
    03282   if (v->current_order.IsType(OT_LOADING)) {
    03283     RecalcFrozen(Station::Get(v->last_station_visited));
    03284   }
    03285 }
    03286
    03292 void RecalcFrozen(Station *st)
    03293 {
    03294   for (CargoID cargo = 0; cargo < NUM_CARGO; ++cargo) {
    03295     LinkStatMap &links = st->goods[cargo].link_stats;
    03296     for (LinkStatMap::iterator i = links.begin(); i != links.end(); ++i) {
    03297       i->second.Unfreeze();
    03298     }
    03299   }
    03300
    03301   std::list<Vehicle *>::iterator v_it = st->loading_vehicles.begin();
    03302   while (v_it != st->loading_vehicles.end()) {
    03303     const Vehicle *front = *v_it;
    03304     OrderList *orders = front->orders.list;
    03305     if (orders != NULL) {
    03306       StationID next_station_id = orders->GetNextStoppingStation(front->cur_implicit_order_index, st->index);
    03307       if (next_station_id != INVALID_STATION && next_station_id != st->index) {
    03308         IncreaseStats(st, front, next_station_id, true);
    03309       }
    03310     }
    03311     ++v_it;
    03312   }
    03313 }
    03314
    03322 void DecreaseFrozen(Station *st, const Vehicle *front, StationID next_station_id) {
    03323   assert(st->index != next_station_id);
    03324   assert(next_station_id != INVALID_STATION);
    03325   for (const Vehicle *v = front; v != NULL; v = v->Next()) {
    03326     if (v->cargo_cap <= 0) continue;
    03327
    03328     LinkStatMap &link_stats = st->goods[v->cargo_type].link_stats;
    03329     LinkStatMap::iterator lstat_it = link_stats.find(next_station_id);
    03330     if (lstat_it == link_stats.end()) {
    03331       DEBUG(misc, 1, "frozen not in linkstat list.");
    03332       RecalcFrozen(st);
    03333       return;
    03334     }
    03335
    03336     LinkStat &link_stat = lstat_it->second;
    03337     if (link_stat.Frozen() < v->cargo_cap) {
    03338       DEBUG(misc, 1, "frozen is smaller than cargo cap.");
    03339       RecalcFrozen(st);
    03340       return;
    03341     }
    03342     link_stat.Unfreeze(v->cargo_cap);
    03343     assert(!link_stat.IsNull());
    03344   }
    03345 }
    03346
    03355 void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id, bool freeze)
    03356 {
    03357   Station *next = Station::GetIfValid(next_station_id);
    03358   assert(st->index != next_station_id && next != NULL);
    03359   uint average_length = GetMovingAverageLength(st, next);
    03360
    03361   for (const Vehicle *v = front; v != NULL; v = v->Next()) {
    03362     if (v->cargo_cap > 0) {
    03363       LinkStatMap &stats = st->goods[v->cargo_type].link_stats;
    03364       LinkStatMap::iterator i = stats.find(next_station_id);
    03365       if (i == stats.end()) {
    03366         stats.insert(std::make_pair(next_station_id, LinkStat(average_length,
    03367             v->cargo_cap, freeze ? v->cargo_cap : 0, freeze ? 0 : v->cargo.Count())));
    03368       } else {
    03369         LinkStat &link_stat = i->second;
    03370         if (freeze) {
    03371           link_stat.Freeze(v->cargo_cap);
    03372         } else {
    03373           link_stat.Increase(v->cargo_cap, v->cargo.Count());
    03374         }
    03375         assert(!link_stat.IsNull());
    03376       }
    03377     }
    03378   }
    03379 }
    03380
    03381 /* Called for every station each tick. */
    03382 static void StationHandleSmallTick(BaseStation *st)
    03383 {
    03384   if ((st->facilities & FACIL_WAYPOINT) != 0 || !st->IsInUse()) return;
    03385
    03386   byte b = st->delete_ctr + 1;
    03387   if (b >= STATION_RATING_TICKS) b = 0;
    03388   st->delete_ctr = b;
    03389
    03390   if (b == 0) UpdateStationRating(Station::From(st));
    03391 }
    03392
    03393 void OnTick_Station()
    03394 {
    03395   if (_game_mode == GM_EDITOR) return;
    03396
    03397   RunAverages<Station>();
    03398
    03399   BaseStation *st;
    03400   FOR_ALL_BASE_STATIONS(st) {
    03401     StationHandleSmallTick(st);
    03402
    03403     /* Run STATION_ACCEPTANCE_TICKS = 250 tick interval trigger for station animation.
    03404      * Station index is included so that triggers are not all done
    03405      * at the same time. */
    03406     if ((_tick_counter + st->index) % STATION_ACCEPTANCE_TICKS == 0) {
    03407       /* Stop processing this station if it was deleted */
    03408       if (!StationHandleBigTick(st)) continue;
    03409       TriggerStationAnimation(st, st->xy, SAT_250_TICKS);
    03410       if (Station::IsExpected(st)) AirportAnimationTrigger(Station::From(st), AAT_STATION_250_TICKS);
    03411     }
    03412   }
    03413 }
    03414
    03416 void StationMonthlyLoop()
    03417 {
    03418   Station *st;
    03419
    03420   FOR_ALL_STATIONS(st) {
    03421     for(int goods_index = 0; goods_index < NUM_CARGO; ++goods_index) {
    03422       st->goods[goods_index].supply = st->goods[goods_index].supply_new;
    03423       st->goods[goods_index].supply_new = 0;
    03424     }
    03425   }
    03426
    03427   FOR_ALL_STATIONS(st) {
    03428     for (CargoID i = 0; i < NUM_CARGO; i++) {
    03429       GoodsEntry *ge = &st->goods[i];
    03430       SB(ge->acceptance_pickup, GoodsEntry::GES_LAST_MONTH, 1, GB(ge->acceptance_pickup, GoodsEntry::GES_CURRENT_MONTH, 1));
    03431       ClrBit(ge->acceptance_pickup, GoodsEntry::GES_CURRENT_MONTH);
    03432     }
    03433   }
    03434 }
    03435
    03436
    03437 void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
    03438 {
    03439   Station *st;
    03440
    03441   FOR_ALL_STATIONS(st) {
    03442     if (st->owner == owner &&
    03443         DistanceManhattan(tile, st->xy) <= radius) {
    03444       for (CargoID i = 0; i < NUM_CARGO; i++) {
    03445         GoodsEntry *ge = &st->goods[i];
    03446
    03447         if (ge->acceptance_pickup != 0) {
    03448           ge->rating = Clamp(ge->rating + amount, 0, 255);
    03449         }
    03450       }
    03451     }
    03452   }
    03453 }
    03454
    03455 static uint UpdateStationWaiting(Station *st, CargoID type, uint amount, SourceType source_type, SourceID source_id)
    03456 {
    03457   /* We can't allocate a CargoPacket? Then don't do anything
    03458    * at all; i.e. just discard the incoming cargo. */
    03459   if (!CargoPacket::CanAllocateItem()) return 0;
    03460
    03461   GoodsEntry &ge = st->goods[type];
    03462   amount += ge.amount_fract;
    03463   ge.amount_fract = GB(amount, 0, 8);
    03464
    03465   amount >>= 8;
    03466   /* No new "real" cargo item yet. */
    03467   if (amount == 0) return 0;
    03468
    03469   StationID id = st->index;
    03470   StationID next = INVALID_STATION;
    03471   FlowStatSet &flow_stats = ge.flows[id];
    03472   FlowStatSet::iterator i = flow_stats.begin();
    03473   if (i != flow_stats.end()) {
    03474     next = i->Via();
    03475     const FlowStat &f = *i;
    03476
    03477     if (_settings_game.linkgraph.no_overload_links &&
    03478         _settings_game.linkgraph.GetDistributionType(type) == DT_SYMMETRIC) {
    03479       int limit = f.Planned() - f.Sent();
    03480
    03481       LinkStatMap::const_iterator it = ge.link_stats.find(next);
    03482       const LinkStat &ls = (it->second);
    03483
    03484       /* Don't send more than was planned or if the link's usage is higher than permitted. */
    03485       limit = min(limit, (ls.Capacity() * _settings_game.linkgraph.short_path_saturation) / 100 - ls.Usage());
    03486
    03487       if (limit < 0) {
    03488         limit = 0;
    03489       }
    03490
    03491       if (amount > (uint)limit) {
    03492         amount = (uint)limit;
    03493       }
    03494     }
    03495
    03496     ge.UpdateFlowStats(flow_stats, i, amount);
    03497
    03498     if (amount == 0) {
    03499       return amount;
    03500     }
    03501   }
    03502
    03503   ge.cargo.Append(next, new CargoPacket(st->index, st->xy, amount, source_type, source_id));
    03504   ge.supply_new += amount;
    03505
    03506   if (!HasBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP)) {
    03507     InvalidateWindowData(WC_STATION_LIST, st->index);
    03508     SetBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP);
    03509   }
    03510
    03511   TriggerStationAnimation(st, st->xy, SAT_NEW_CARGO, type);
    03512   AirportAnimationTrigger(st, AAT_STATION_NEW_CARGO, type);
    03513
    03514   SetWindowDirty(WC_STATION_VIEW, st->index);
    03515   st->MarkTilesDirty(true);
    03516   return amount;
    03517 }
    03518
    03519 static bool IsUniqueStationName(const char *name)
    03520 {
    03521   const Station *st;
    03522
    03523   FOR_ALL_STATIONS(st) {
    03524     if (st->name != NULL && strcmp(st->name, name) == 0) return false;
    03525   }
    03526
    03527   return true;
    03528 }
    03529
    03539 CommandCost CmdRenameStation(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    03540 {
    03541   Station *st = Station::GetIfValid(p1);
    03542   if (st == NULL) return CMD_ERROR;
    03543
    03544   CommandCost ret = CheckOwnership(st->owner);
    03545   if (ret.Failed()) return ret;
    03546
    03547   bool reset = StrEmpty(text);
    03548
    03549   if (!reset) {
    03550     if (Utf8StringLength(text) >= MAX_LENGTH_STATION_NAME_CHARS) return CMD_ERROR;
    03551     if (!IsUniqueStationName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
    03552   }
    03553
    03554   if (flags & DC_EXEC) {
    03555     free(st->name);
    03556     st->name = reset ? NULL : strdup(text);
    03557
    03558     st->UpdateVirtCoord();
    03559     InvalidateWindowData(WC_STATION_LIST, st->owner, 1);
    03560   }
    03561
    03562   return CommandCost();
    03563 }
    03564
    03571 void FindStationsAroundTiles(const TileArea &location, StationList *stations)
    03572 {
    03573   /* area to search = producer plus station catchment radius */
    03574   int max_rad = (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED);
    03575
    03576   for (int dy = -max_rad; dy < location.h + max_rad; dy++) {
    03577     for (int dx = -max_rad; dx < location.w + max_rad; dx++) {
    03578       TileIndex cur_tile = TileAddWrap(location.tile, dx, dy);
    03579       if (cur_tile == INVALID_TILE || !IsTileType(cur_tile, MP_STATION)) continue;
    03580
    03581       Station *st = Station::GetByTile(cur_tile);
    03582       if (st == NULL) continue;
    03583
    03584       if (_settings_game.station.modified_catchment) {
    03585         int rad = st->GetCatchmentRadius();
    03586         if (dx < -rad || dx >= rad + location.w || dy < -rad || dy >= rad + location.h) continue;
    03587       }
    03588
    03589       /* Insert the station in the set. This will fail if it has
    03590        * already been added.
    03591        */
    03592       stations->Include(st);
    03593     }
    03594   }
    03595 }
    03596
    03601 const StationList *StationFinder::GetStations()
    03602 {
    03603   if (this->tile != INVALID_TILE) {
    03604     FindStationsAroundTiles(*this, &this->stations);
    03605     this->tile = INVALID_TILE;
    03606   }
    03607   return &this->stations;
    03608 }
    03609
    03610 uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList *all_stations)
    03611 {
    03612   /* Return if nothing to do. Also the rounding below fails for 0. */
    03613   if (amount == 0) return 0;
    03614
    03615   Station *st1 = NULL;   // Station with best rating
    03616   Station *st2 = NULL;   // Second best station
    03617   uint best_rating1 = 0; // rating of st1
    03618   uint best_rating2 = 0; // rating of st2
    03619
    03620   for (Station * const *st_iter = all_stations->Begin(); st_iter != all_stations->End(); ++st_iter) {
    03621     Station *st = *st_iter;
    03622
    03623     /* Is the station reserved exclusively for somebody else? */
    03624     if (st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) continue;
    03625
    03626     if (st->goods[type].rating == 0) continue; // Lowest possible rating, better not to give cargo anymore
    03627
    03628     if (_settings_game.order.selectgoods && st->goods[type].last_speed == 0) continue; // Selectively servicing stations, and not this one
    03629
    03630     if (IsCargoInClass(type, CC_PASSENGERS)) {
    03631       if (st->facilities == FACIL_TRUCK_STOP) continue; // passengers are never served by just a truck stop
    03632     } else {
    03633       if (st->facilities == FACIL_BUS_STOP) continue; // non-passengers are never served by just a bus stop
    03634     }
    03635
    03636     /* This station can be used, add it to st1/st2 */
    03637     if (st1 == NULL || st->goods[type].rating >= best_rating1) {
    03638       st2 = st1; best_rating2 = best_rating1; st1 = st; best_rating1 = st->goods[type].rating;
    03639     } else if (st2 == NULL || st->goods[type].rating >= best_rating2) {
    03640       st2 = st; best_rating2 = st->goods[type].rating;
    03641     }
    03642   }
    03643
    03644   /* no stations around at all? */
    03645   if (st1 == NULL) return 0;
    03646
    03647   /* From now we'll calculate with fractal cargo amounts.
    03648    * First determine how much cargo we really have. */
    03649   amount *= best_rating1 + 1;
    03650
    03651   if (st2 == NULL) {
    03652     /* only one station around */
    03653     return UpdateStationWaiting(st1, type, amount, source_type, source_id);
    03654   }
    03655
    03656   /* several stations around, the best two (highest rating) are in st1 and st2 */
    03657   assert(st1 != NULL);
    03658   assert(st2 != NULL);
    03659   assert(best_rating1 != 0 || best_rating2 != 0);
    03660
    03661   /* Then determine the amount the worst station gets. We do it this way as the
    03662    * best should get a bonus, which in this case is the rounding difference from
    03663    * this calculation. In reality that will mean the bonus will be pretty low.
    03664    * Nevertheless, the best station should always get the most cargo regardless
    03665    * of rounding issues. */
    03666   uint worst_cargo = amount * best_rating2 / (best_rating1 + best_rating2);
    03667   assert(worst_cargo <= (amount - worst_cargo));
    03668
    03669   /* And then send the cargo to the stations! */
    03670   uint moved = UpdateStationWaiting(st1, type, amount - worst_cargo, source_type, source_id);
    03671   /* These two UpdateStationWaiting's can't be in the statement as then the order
    03672    * of execution would be undefined and that could cause desyncs with callbacks. */
    03673   return moved + UpdateStationWaiting(st2, type, worst_cargo, source_type, source_id);
    03674 }
    03675
    03676 void BuildOilRig(TileIndex tile)
    03677 {
    03678   if (!Station::CanAllocateItem()) {
    03679     DEBUG(misc, 0, "Can't allocate station for oilrig at 0x%X, reverting to oilrig only", tile);
    03680     return;
    03681   }
    03682
    03683   Station *st = new Station(tile);
    03684   st->town = ClosestTownFromTile(tile, UINT_MAX);
    03685
    03686   st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
    03687
    03688   assert(IsTileType(tile, MP_INDUSTRY));
    03689   DeleteAnimatedTile(tile);
    03690   MakeOilrig(tile, st->index, GetWaterClass(tile));
    03691
    03692   st->owner = OWNER_NONE;
    03693   st->airport.type = AT_OILRIG;
    03694   st->airport.Add(tile);
    03695   st->dock_tile = tile;
    03696   st->facilities = FACIL_AIRPORT | FACIL_DOCK;
    03697   st->build_date = _date;
    03698
    03699   st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
    03700
    03701   for (CargoID j = 0; j < NUM_CARGO; j++) {
    03702     st->goods[j].acceptance_pickup = 0;
    03703     st->goods[j].days_since_pickup = 255;
    03704     st->goods[j].rating = INITIAL_STATION_RATING;
    03705     st->goods[j].last_speed = 0;
    03706     st->goods[j].last_age = 255;
    03707   }
    03708
    03709   st->UpdateVirtCoord();
    03710   UpdateStationAcceptance(st, false);
    03711   st->RecomputeIndustriesNear();
    03712 }
    03713
    03714 void DeleteOilRig(TileIndex tile)
    03715 {
    03716   Station *st = Station::GetByTile(tile);
    03717
    03718   MakeWaterKeepingClass(tile, OWNER_NONE);
    03719
    03720   st->dock_tile = INVALID_TILE;
    03721   st->airport.Clear();
    03722   st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
    03723   st->airport.flags = 0;
    03724
    03725   st->rect.AfterRemoveTile(st, tile);
    03726
    03727   st->UpdateVirtCoord();
    03728   st->RecomputeIndustriesNear();
    03729   if (!st->IsInUse()) delete st;
    03730 }
    03731
    03732 static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
    03733 {
    03734   if (IsDriveThroughStopTile(tile)) {
    03735     for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
    03736       /* Update all roadtypes, no matter if they are present */
    03737       if (GetRoadOwner(tile, rt) == old_owner) {
    03738         SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
    03739       }
    03740     }
    03741   }
    03742
    03743   if (!IsTileOwner(tile, old_owner)) return;
    03744
    03745   if (new_owner != INVALID_OWNER) {
    03746     /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
    03747     SetTileOwner(tile, new_owner);
    03748     InvalidateWindowClassesData(WC_STATION_LIST, 0);
    03749   } else {
    03750     if (IsDriveThroughStopTile(tile)) {
    03751       /* Remove the drive-through road stop */
    03752       DoCommand(tile, 1 | 1 << 8, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, DC_EXEC | DC_BANKRUPT, CMD_REMOVE_ROAD_STOP);
    03753       assert(IsTileType(tile, MP_ROAD));
    03754       /* Change owner of tile and all roadtypes */
    03755       ChangeTileOwner(tile, old_owner, new_owner);
    03756     } else {
    03757       DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
    03758       /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
    03759        * Update owner of buoy if it was not removed (was in orders).
    03760        * Do not update when owned by OWNER_WATER (sea and rivers). */
    03761       if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
    03762     }
    03763   }
    03764 }
    03765
    03774 static bool CanRemoveRoadWithStop(TileIndex tile, DoCommandFlag flags)
    03775 {
    03776   /* Yeah... water can always remove stops, right? */
    03777   if (_current_company == OWNER_WATER) return true;
    03778
    03779   RoadTypes rts = GetRoadTypes(tile);
    03780   if (HasBit(rts, ROADTYPE_TRAM)) {
    03781     Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
    03782     if (tram_owner != OWNER_NONE && CheckOwnership(tram_owner).Failed()) return false;
    03783   }
    03784   if (HasBit(rts, ROADTYPE_ROAD)) {
    03785     Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
    03786     if (road_owner != OWNER_TOWN) {
    03787       if (road_owner != OWNER_NONE && CheckOwnership(road_owner).Failed()) return false;
    03788     } else {
    03789       if (CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_ROAD), OWNER_TOWN, ROADTYPE_ROAD, flags).Failed()) return false;
    03790     }
    03791   }
    03792
    03793   return true;
    03794 }
    03795
    03802 CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
    03803 {
    03804   if (flags & DC_AUTO) {
    03805     switch (GetStationType(tile)) {
    03806       default: break;
    03807       case STATION_RAIL:     return_cmd_error(STR_ERROR_MUST_DEMOLISH_RAILROAD);
    03808       case STATION_WAYPOINT: return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
    03809       case STATION_AIRPORT:  return_cmd_error(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
    03810       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);
    03811       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);
    03812       case STATION_BUOY:     return_cmd_error(STR_ERROR_BUOY_IN_THE_WAY);
    03813       case STATION_DOCK:     return_cmd_error(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
    03814       case STATION_OILRIG:
    03815         SetDParam(1, STR_INDUSTRY_NAME_OIL_RIG);
    03816         return_cmd_error(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY);
    03817     }
    03818   }
    03819
    03820   switch (GetStationType(tile)) {
    03821     case STATION_RAIL:     return RemoveRailStation(tile, flags);
    03822     case STATION_WAYPOINT: return RemoveRailWaypoint(tile, flags);
    03823     case STATION_AIRPORT:  return RemoveAirport(tile, flags);
    03824     case STATION_TRUCK:
    03825       if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags)) {
    03826         return_cmd_error(STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
    03827       }
    03828       return RemoveRoadStop(tile, flags);
    03829     case STATION_BUS:
    03830       if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags)) {
    03831         return_cmd_error(STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
    03832       }
    03833       return RemoveRoadStop(tile, flags);
    03834     case STATION_BUOY:     return RemoveBuoy(tile, flags);
    03835     case STATION_DOCK:     return RemoveDock(tile, flags);
    03836     default: break;
    03837   }
    03838
    03839   return CMD_ERROR;
    03840 }
    03841
    03842 static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
    03843 {
    03844   if (_settings_game.construction.build_on_slopes && AutoslopeEnabled()) {
    03845     /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
    03846      *       TTDP does not call it.
    03847      */
    03848     if (!IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new))) {
    03849       switch (GetStationType(tile)) {
    03850         case STATION_WAYPOINT:
    03851         case STATION_RAIL: {
    03852           DiagDirection direction = AxisToDiagDir(GetRailStationAxis(tile));
    03853           if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
    03854           if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
    03855           return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
    03856         }
    03857
    03858         case STATION_AIRPORT:
    03859           return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
    03860
    03861         case STATION_TRUCK:
    03862         case STATION_BUS: {
    03863           DiagDirection direction = GetRoadStopDir(tile);
    03864           if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
    03865           if (IsDriveThroughStopTile(tile)) {
    03866             if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
    03867           }
    03868           return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
    03869         }
    03870
    03871         default: break;
    03872       }
    03873     }
    03874   }
    03875   return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    03876 }
    03877
    03884 void GoodsEntry::UpdateFlowStats(FlowStatSet &flow_stats, FlowStatSet::iterator flow_it, uint count)
    03885 {
    03886   FlowStat fs = *flow_it;
    03887   fs.Increase(count);
    03888   flow_stats.erase(flow_it);
    03889   flow_stats.insert(fs);
    03890 }
    03891
    03898 void GoodsEntry::UpdateFlowStats(FlowStatSet &flow_stats, uint count, StationID next)
    03899 {
    03900   FlowStatSet::iterator flow_it = flow_stats.begin();
    03901   while (flow_it != flow_stats.end()) {
    03902     StationID via = flow_it->Via();
    03903     if (via == next) { //usually the first one is the correct one
    03904       this->UpdateFlowStats(flow_stats, flow_it, count);
    03905       return;
    03906     } else {
    03907       ++flow_it;
    03908     }
    03909   }
    03910 }
    03911
    03918 void GoodsEntry::UpdateFlowStats(StationID source, uint count, StationID next)
    03919 {
    03920   if (source == INVALID_STATION || next == INVALID_STATION || this->flows.empty()) return;
    03921   FlowStatSet &flow_stats = this->flows[source];
    03922   this->UpdateFlowStats(flow_stats, count, next);
    03923 }
    03924
    03932 StationID GoodsEntry::UpdateFlowStatsTransfer(StationID source, uint count, StationID curr)
    03933 {
    03934   if (source == INVALID_STATION || this->flows.empty()) return INVALID_STATION;
    03935   FlowStatSet &flow_stats = this->flows[source];
    03936   FlowStatSet::iterator flow_it = flow_stats.begin();
    03937   while (flow_it != flow_stats.end()) {
    03938     StationID via = flow_it->Via();
    03939     if (via != curr) {
    03940       this->UpdateFlowStats(flow_stats, flow_it, count);
    03941       return via;
    03942     } else {
    03943       ++flow_it;
    03944     }
    03945   }
    03946   return INVALID_STATION;
    03947 }
    03948
    03954 FlowStat GoodsEntry::GetSumFlowVia(StationID via) const {
    03955   FlowStat ret(1, via);
    03956   for (FlowStatMap::const_iterator i = this->flows.begin(); i != this->flows.end(); ++i) {
    03957     const FlowStatSet &flow_set = i->second;
    03958     for (FlowStatSet::const_iterator j = flow_set.begin(); j != flow_set.end(); ++j) {
    03959       const FlowStat &flow = *j;
    03960       if (flow.Via() == via) {
    03961         ret += flow;
    03962       }
    03963     }
    03964   }
    03965   return ret;
    03966 }
    03967
    03968 extern const TileTypeProcs _tile_type_station_procs = {
    03969   DrawTile_Station,           // draw_tile_proc
    03970   GetSlopeZ_Station,          // get_slope_z_proc
    03971   ClearTile_Station,          // clear_tile_proc
    03972   NULL,                       // add_accepted_cargo_proc
    03973   GetTileDesc_Station,        // get_tile_desc_proc
    03974   GetTileTrackStatus_Station, // get_tile_track_status_proc
    03975   ClickTile_Station,          // click_tile_proc
    03976   AnimateTile_Station,        // animate_tile_proc
    03977   TileLoop_Station,           // tile_loop_clear
    03978   ChangeTileOwner_Station,    // change_tile_owner_clear
    03979   NULL,                       // add_produced_cargo_proc
    03980   VehicleEnter_Station,       // vehicle_enter_tile_proc
    03981   GetFoundation_Station,      // get_foundation_proc
    03982   TerraformTile_Station,      // terraform_tile_proc
    03983 };