station_cmd.cpp

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

Generated on Mon Mar 9 23:33:51 2009 for openttd by  doxygen 1.5.6