# station_cmd.cpp

Go to the documentation of this file.

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