# station_cmd.cpp

Go to the documentation of this file.

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