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 };

Generated on Mon Feb 16 23:12:10 2009 for openttd by  doxygen 1.5.6