# station_cmd.cpp

Go to the documentation of this file.

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