station_cmd.cpp

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

Generated on Tue Jul 21 18:48:27 2009 for OpenTTD by  doxygen 1.5.6