# station_cmd.cpp

Go to the documentation of this file.

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