station_cmd.cpp

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

Generated on Wed Oct 1 17:03:23 2008 for openttd by  doxygen 1.5.6