vehicle.cpp

Go to the documentation of this file.
00001 /* $Id: vehicle.cpp 18642 2009-12-27 09:05:00Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "gui.h"
00014 #include "openttd.h"
00015 #include "debug.h"
00016 #include "roadveh.h"
00017 #include "ship.h"
00018 #include "spritecache.h"
00019 #include "landscape.h"
00020 #include "timetable.h"
00021 #include "viewport_func.h"
00022 #include "news_func.h"
00023 #include "command_func.h"
00024 #include "company_func.h"
00025 #include "vehicle_gui.h"
00026 #include "train.h"
00027 #include "aircraft.h"
00028 #include "newgrf_engine.h"
00029 #include "newgrf_sound.h"
00030 #include "newgrf_station.h"
00031 #include "group.h"
00032 #include "group_gui.h"
00033 #include "strings_func.h"
00034 #include "zoom_func.h"
00035 #include "functions.h"
00036 #include "date_func.h"
00037 #include "window_func.h"
00038 #include "vehicle_func.h"
00039 #include "autoreplace_func.h"
00040 #include "autoreplace_gui.h"
00041 #include "station_base.h"
00042 #include "ai/ai.hpp"
00043 #include "core/smallmap_type.hpp"
00044 #include "depot_func.h"
00045 #include "network/network.h"
00046 #include "core/pool_func.hpp"
00047 #include "economy_base.h"
00048 #include "articulated_vehicles.h"
00049 #include "roadstop_base.h"
00050 
00051 #include "table/sprites.h"
00052 #include "table/strings.h"
00053 
00054 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00055 
00056 VehicleID _vehicle_id_ctr_day;
00057 const Vehicle *_place_clicked_vehicle;
00058 VehicleID _new_vehicle_id;
00059 uint16 _returned_refit_capacity;
00060 byte _age_cargo_skip_counter; 
00061 
00062 
00063 /* Initialize the vehicle-pool */
00064 VehiclePool _vehicle_pool("Vehicle");
00065 INSTANTIATE_POOL_METHODS(Vehicle)
00066 
00067 
00071 bool Vehicle::NeedsAutorenewing(const Company *c) const
00072 {
00073   /* We can always generate the Company pointer when we have the vehicle.
00074    * However this takes time and since the Company pointer is often present
00075    * when this function is called then it's faster to pass the pointer as an
00076    * argument rather than finding it again. */
00077   assert(c == Company::Get(this->owner));
00078 
00079   if (!c->settings.engine_renew) return false;
00080   if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00081   if (this->age == 0) return false; // rail cars don't age and lacks a max age
00082 
00083   return true;
00084 }
00085 
00086 void VehicleServiceInDepot(Vehicle *v)
00087 {
00088   v->date_of_last_service = _date;
00089   v->breakdowns_since_last_service = 0;
00090   v->reliability = Engine::Get(v->engine_type)->reliability;
00091   SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
00092 }
00093 
00094 bool Vehicle::NeedsServicing() const
00095 {
00096   /* Stopped or crashed vehicles will not move, as such making unmovable
00097    * vehicles to go for service is lame. */
00098   if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00099 
00100   /* Are we ready for the next service cycle? */
00101   const Company *c = Company::Get(this->owner);
00102   if (c->settings.vehicle.servint_ispercent ?
00103       (this->reliability >= Engine::Get(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00104       (this->date_of_last_service + this->service_interval >= _date)) {
00105     return false;
00106   }
00107 
00108   /* If we're servicing anyway, because we have not disabled servicing when
00109    * there are no breakdowns or we are playing with breakdowns, bail out. */
00110   if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00111       _settings_game.difficulty.vehicle_breakdowns != 0) {
00112     return true;
00113   }
00114 
00115   /* Test whether there is some pending autoreplace.
00116    * Note: We do this after the service-interval test.
00117    * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
00118   bool pending_replace = false;
00119   Money needed_money = c->settings.engine_renew_money;
00120   if (needed_money > c->money) return false;
00121 
00122   for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00123     EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00124 
00125     /* Check engine availability */
00126     if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00127 
00128     /* Check refittability */
00129     uint32 available_cargo_types, union_mask;
00130     GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00131     /* Is there anything to refit? */
00132     if (union_mask != 0) {
00133       CargoID cargo_type;
00134       /* We cannot refit to mixed cargos in an automated way */
00135       if (IsArticulatedVehicleCarryingDifferentCargos(v, &cargo_type)) continue;
00136 
00137       /* Did the old vehicle carry anything? */
00138       if (cargo_type != CT_INVALID) {
00139         /* We can't refit the vehicle to carry the cargo we want */
00140         if (!HasBit(available_cargo_types, cargo_type)) continue;
00141       }
00142     }
00143 
00144     /* Check money.
00145      * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
00146     pending_replace = true;
00147     needed_money += 2 * Engine::Get(new_engine)->GetCost();
00148     if (needed_money > c->money) return false;
00149   }
00150 
00151   return pending_replace;
00152 }
00153 
00154 bool Vehicle::NeedsAutomaticServicing() const
00155 {
00156   if (_settings_game.order.gotodepot && VehicleHasDepotOrders(this)) return false;
00157   if (this->current_order.IsType(OT_LOADING))            return false;
00158   if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00159   return NeedsServicing();
00160 }
00161 
00162 uint Vehicle::Crash(bool flooded)
00163 {
00164   assert((this->vehstatus & VS_CRASHED) == 0);
00165   assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
00166 
00167   uint pass = 0;
00168   /* crash all wagons, and count passengers */
00169   for (Vehicle *v = this; v != NULL; v = v->Next()) {
00170     if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00171     v->vehstatus |= VS_CRASHED;
00172     MarkSingleVehicleDirty(v);
00173   }
00174 
00175   /* Dirty some windows */
00176   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00177   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
00178   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00179   SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00180 
00181   return pass;
00182 }
00183 
00184 
00193 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00194 {
00195   const Engine *e = Engine::Get(engine);
00196   uint32 grfid = e->grffile->grfid;
00197   GRFConfig *grfconfig = GetGRFConfig(grfid);
00198 
00199   if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00200     SetBit(grfconfig->grf_bugs, bug_type);
00201     SetDParamStr(0, grfconfig->name);
00202     SetDParam(1, engine);
00203     ShowErrorMessage(part1, part2, 0, 0, true);
00204     if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00205   }
00206 
00207   /* debug output */
00208   char buffer[512];
00209 
00210   SetDParamStr(0, grfconfig->name);
00211   GetString(buffer, part1, lastof(buffer));
00212   DEBUG(grf, 0, "%s", buffer + 3);
00213 
00214   SetDParam(1, engine);
00215   GetString(buffer, part2, lastof(buffer));
00216   DEBUG(grf, 0, "%s", buffer + 3);
00217 }
00218 
00219 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00220 {
00221   byte z = *(byte*)data;
00222 
00223   if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00224   if (v->z_pos > z) return NULL;
00225 
00226   _error_message = STR_ERROR_TRAIN_IN_THE_WAY + v->type;
00227   return v;
00228 }
00229 
00230 bool EnsureNoVehicleOnGround(TileIndex tile)
00231 {
00232   byte z = GetTileMaxZ(tile);
00233   return !HasVehicleOnPos(tile, &z, &EnsureNoVehicleProcZ);
00234 }
00235 
00237 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00238 {
00239   if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00240   if (v == (const Vehicle *)data) return NULL;
00241 
00242   _error_message = STR_ERROR_TRAIN_IN_THE_WAY + v->type;
00243   return v;
00244 }
00245 
00253 bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00254 {
00255   return HasVehicleOnPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc) ||
00256       HasVehicleOnPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc);
00257 }
00258 
00259 
00260 Vehicle::Vehicle(VehicleType type)
00261 {
00262   this->type               = type;
00263   this->coord.left         = INVALID_COORD;
00264   this->group_id           = DEFAULT_GROUP;
00265   this->fill_percent_te_id = INVALID_TE_ID;
00266   this->first              = this;
00267   this->colourmap          = PAL_NONE;
00268 }
00269 
00274 byte VehicleRandomBits()
00275 {
00276   return GB(Random(), 0, 8);
00277 }
00278 
00279 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
00280  * lookup times at the expense of memory usage. */
00281 const int HASH_BITS = 7;
00282 const int HASH_SIZE = 1 << HASH_BITS;
00283 const int HASH_MASK = HASH_SIZE - 1;
00284 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00285 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00286 
00287 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
00288  * Profiling results show that 0 is fastest. */
00289 const int HASH_RES = 0;
00290 
00291 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00292 
00293 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00294 {
00295   for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00296     for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00297       Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00298       for (; v != NULL; v = v->next_new_hash) {
00299         Vehicle *a = proc(v, data);
00300         if (find_first && a != NULL) return a;
00301       }
00302       if (x == xu) break;
00303     }
00304     if (y == yu) break;
00305   }
00306 
00307   return NULL;
00308 }
00309 
00310 
00322 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00323 {
00324   const int COLL_DIST = 6;
00325 
00326   /* Hash area to scan is from xl,yl to xu,yu */
00327   int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00328   int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00329   int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00330   int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00331 
00332   return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00333 }
00334 
00349 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00350 {
00351   VehicleFromPosXY(x, y, data, proc, false);
00352 }
00353 
00365 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00366 {
00367   return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00368 }
00369 
00380 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00381 {
00382   int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00383   int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00384 
00385   Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00386   for (; v != NULL; v = v->next_new_hash) {
00387     if (v->tile != tile) continue;
00388 
00389     Vehicle *a = proc(v, data);
00390     if (find_first && a != NULL) return a;
00391   }
00392 
00393   return NULL;
00394 }
00395 
00409 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00410 {
00411   VehicleFromPos(tile, data, proc, false);
00412 }
00413 
00424 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00425 {
00426   return VehicleFromPos(tile, data, proc, true) != NULL;
00427 }
00428 
00429 
00430 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00431 {
00432   Vehicle **old_hash = v->old_new_hash;
00433   Vehicle **new_hash;
00434 
00435   if (remove) {
00436     new_hash = NULL;
00437   } else {
00438     int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00439     int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00440     new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00441   }
00442 
00443   if (old_hash == new_hash) return;
00444 
00445   /* Remove from the old position in the hash table */
00446   if (old_hash != NULL) {
00447     if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
00448     *v->prev_new_hash = v->next_new_hash;
00449   }
00450 
00451   /* Insert vehicle at beginning of the new position in the hash table */
00452   if (new_hash != NULL) {
00453     v->next_new_hash = *new_hash;
00454     if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
00455     v->prev_new_hash = new_hash;
00456     *new_hash = v;
00457   }
00458 
00459   /* Remember current hash position */
00460   v->old_new_hash = new_hash;
00461 }
00462 
00463 static Vehicle *_vehicle_position_hash[0x1000];
00464 
00465 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00466 {
00467   UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00468 
00469   Vehicle **old_hash, **new_hash;
00470   int old_x = v->coord.left;
00471   int old_y = v->coord.top;
00472 
00473   new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00474   old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00475 
00476   if (old_hash == new_hash) return;
00477 
00478   /* remove from hash table? */
00479   if (old_hash != NULL) {
00480     if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
00481     *v->prev_hash = v->next_hash;
00482   }
00483 
00484   /* insert into hash table? */
00485   if (new_hash != NULL) {
00486     v->next_hash = *new_hash;
00487     if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
00488     v->prev_hash = new_hash;
00489     *new_hash = v;
00490   }
00491 }
00492 
00493 void ResetVehiclePosHash()
00494 {
00495   Vehicle *v;
00496   FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00497   memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00498   memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00499 }
00500 
00501 void ResetVehicleColourMap()
00502 {
00503   Vehicle *v;
00504   FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00505 }
00506 
00511 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00512 static AutoreplaceMap _vehicles_to_autoreplace;
00513 
00514 void InitializeVehicles()
00515 {
00516   _vehicle_pool.CleanPool();
00517   _cargo_payment_pool.CleanPool();
00518 
00519   _age_cargo_skip_counter = 1;
00520 
00521   _vehicles_to_autoreplace.Reset();
00522   ResetVehiclePosHash();
00523 }
00524 
00525 uint CountVehiclesInChain(const Vehicle *v)
00526 {
00527   uint count = 0;
00528   do count++; while ((v = v->Next()) != NULL);
00529   return count;
00530 }
00531 
00535 bool Vehicle::IsEngineCountable() const
00536 {
00537   switch (this->type) {
00538     case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
00539     case VEH_TRAIN:
00540       return !Train::From(this)->IsArticulatedPart() && // tenders and other articulated parts
00541           !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
00542     case VEH_ROAD: return RoadVehicle::From(this)->IsRoadVehFront();
00543     case VEH_SHIP: return true;
00544     default: return false; // Only count company buildable vehicles
00545   }
00546 }
00547 
00548 void Vehicle::PreDestructor()
00549 {
00550   if (CleaningPool()) return;
00551 
00552   if (Station::IsValidID(this->last_station_visited)) {
00553     Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00554 
00555     HideFillingPercent(&this->fill_percent_te_id);
00556 
00557     delete this->cargo_payment;
00558   }
00559 
00560   if (this->IsEngineCountable()) {
00561     Company::Get(this->owner)->num_engines[this->engine_type]--;
00562     if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00563 
00564     DeleteGroupHighlightOfVehicle(this);
00565     if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
00566     if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00567   }
00568 
00569   if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00570     Aircraft *a = Aircraft::From(this);
00571     Station *st = GetTargetAirportIfValid(a);
00572     if (st != NULL) {
00573       const AirportFTA *layout = st->Airport()->layout;
00574       CLRBITS(st->airport_flags, layout[a->previous_pos].block | layout[a->pos].block);
00575     }
00576   }
00577 
00578 
00579   if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00580     RoadVehicle *v = RoadVehicle::From(this);
00581     if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00582       /* Leave the drive through roadstop, when you have not already left it. */
00583       RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00584     }
00585   }
00586 
00587   if (this->Previous() == NULL) {
00588     InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00589   }
00590 
00591   if (this->IsPrimaryVehicle()) {
00592     DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00593     DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00594     DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00595     DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00596     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00597     SetWindowDirty(WC_COMPANY, this->owner);
00598   }
00599   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00600 
00601   this->cargo.Truncate(0);
00602   DeleteVehicleOrders(this);
00603   DeleteDepotHighlightOfVehicle(this);
00604 
00605   extern void StopGlobalFollowVehicle(const Vehicle *v);
00606   StopGlobalFollowVehicle(this);
00607 
00608   ReleaseDisastersTargetingVehicle(this->index);
00609 }
00610 
00611 Vehicle::~Vehicle()
00612 {
00613   free(this->name);
00614 
00615   if (CleaningPool()) return;
00616 
00617   /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
00618    * it may happen that vehicle chain is deleted when visible */
00619   if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00620 
00621   Vehicle *v = this->Next();
00622   this->SetNext(NULL);
00623 
00624   delete v;
00625 
00626   UpdateVehiclePosHash(this, INVALID_COORD, 0);
00627   DeleteVehicleNews(this->index, INVALID_STRING_ID);
00628 }
00629 
00633 void VehicleEnteredDepotThisTick(Vehicle *v)
00634 {
00635   /* Vehicle should stop in the depot if it was in 'stopping' state */
00636   _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00637 
00638   /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
00639    * stopping in the depot, so we stop it to ensure that it will not reserve
00640    * the path out of the depot before we might autoreplace it to a different
00641    * engine. The new engine would not own the reserved path we store that we
00642    * stopped the vehicle, so autoreplace can start it again */
00643   v->vehstatus |= VS_STOPPED;
00644 }
00645 
00651 static void RunVehicleDayProc()
00652 {
00653   if (_game_mode != GM_NORMAL) return;
00654 
00655   /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
00656   for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00657     Vehicle *v = Vehicle::Get(i);
00658     if (v == NULL) continue;
00659 
00660     /* Call the 32-day callback if needed */
00661     if ((v->day_counter & 0x1F) == 0) {
00662       uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00663       if (callback != CALLBACK_FAILED) {
00664         if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
00665         if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00666       }
00667     }
00668 
00669     /* This is called once per day for each vehicle, but not in the first tick of the day */
00670     v->OnNewDay();
00671   }
00672 }
00673 
00674 void CallVehicleTicks()
00675 {
00676   _vehicles_to_autoreplace.Clear();
00677 
00678   _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
00679 
00680   RunVehicleDayProc();
00681 
00682   Station *st;
00683   FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00684 
00685   Vehicle *v;
00686   FOR_ALL_VEHICLES(v) {
00687     /* Vehicle could be deleted in this tick */
00688     if (!v->Tick()) {
00689       assert(Vehicle::Get(vehicle_index) == NULL);
00690       continue;
00691     }
00692 
00693     assert(Vehicle::Get(vehicle_index) == v);
00694 
00695     switch (v->type) {
00696       default: break;
00697 
00698       case VEH_TRAIN:
00699       case VEH_ROAD:
00700       case VEH_AIRCRAFT:
00701       case VEH_SHIP:
00702         if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00703 
00704         if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00705         if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00706         if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsRoadVehFront()) continue;
00707 
00708         v->motion_counter += v->cur_speed;
00709         /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
00710         if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00711 
00712         /* Play an alterate running sound every 16 ticks */
00713         if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00714     }
00715   }
00716 
00717   for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00718     v = it->first;
00719     /* Autoreplace needs the current company set as the vehicle owner */
00720     _current_company = v->owner;
00721 
00722     /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
00723      * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
00724      * they are already leaving the depot again before being replaced. */
00725     if (it->second) v->vehstatus &= ~VS_STOPPED;
00726 
00727     /* Store the position of the effect as the vehicle pointer will become invalid later */
00728     int x = v->x_pos;
00729     int y = v->y_pos;
00730     int z = v->z_pos;
00731 
00732     const Company *c = Company::Get(_current_company);
00733     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00734     CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00735     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00736 
00737     if (!IsLocalCompany()) continue;
00738 
00739     if (res.Succeeded()) {
00740       ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00741       continue;
00742     }
00743 
00744     StringID error_message = res.GetErrorMessage();
00745     if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00746 
00747     if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00748 
00749     StringID message;
00750     if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00751       message = error_message;
00752     } else {
00753       message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00754     }
00755 
00756     SetDParam(0, v->index);
00757     SetDParam(1, error_message);
00758     AddVehicleNewsItem(message, NS_ADVICE, v->index);
00759   }
00760 
00761   _current_company = OWNER_NONE;
00762 }
00763 
00764 static void DoDrawVehicle(const Vehicle *v)
00765 {
00766   SpriteID image = v->cur_image;
00767   SpriteID pal = PAL_NONE;
00768 
00769   if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00770 
00771   AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00772     v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00773 }
00774 
00775 void ViewportAddVehicles(DrawPixelInfo *dpi)
00776 {
00777   /* The bounding rectangle */
00778   const int l = dpi->left;
00779   const int r = dpi->left + dpi->width;
00780   const int t = dpi->top;
00781   const int b = dpi->top + dpi->height;
00782 
00783   /* The hash area to scan */
00784   int xl, xu, yl, yu;
00785 
00786   if (dpi->width + 70 < (1 << (7 + 6))) {
00787     xl = GB(l - 70, 7, 6);
00788     xu = GB(r,      7, 6);
00789   } else {
00790     /* scan whole hash row */
00791     xl = 0;
00792     xu = 0x3F;
00793   }
00794 
00795   if (dpi->height + 70 < (1 << (6 + 6))) {
00796     yl = GB(t - 70, 6, 6) << 6;
00797     yu = GB(b,      6, 6) << 6;
00798   } else {
00799     /* scan whole column */
00800     yl = 0;
00801     yu = 0x3F << 6;
00802   }
00803 
00804   for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00805     for (int x = xl;; x = (x + 1) & 0x3F) {
00806       const Vehicle *v = _vehicle_position_hash[x + y]; // already masked & 0xFFF
00807 
00808       while (v != NULL) {
00809         if (!(v->vehstatus & VS_HIDDEN) &&
00810             l <= v->coord.right &&
00811             t <= v->coord.bottom &&
00812             r >= v->coord.left &&
00813             b >= v->coord.top) {
00814           DoDrawVehicle(v);
00815         }
00816         v = v->next_hash;
00817       }
00818 
00819       if (x == xu) break;
00820     }
00821 
00822     if (y == yu) break;
00823   }
00824 }
00825 
00826 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00827 {
00828   Vehicle *found = NULL, *v;
00829   uint dist, best_dist = UINT_MAX;
00830 
00831   if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00832 
00833   x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00834   y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00835 
00836   FOR_ALL_VEHICLES(v) {
00837     if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00838         x >= v->coord.left && x <= v->coord.right &&
00839         y >= v->coord.top && y <= v->coord.bottom) {
00840 
00841       dist = max(
00842         abs(((v->coord.left + v->coord.right) >> 1) - x),
00843         abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00844       );
00845 
00846       if (dist < best_dist) {
00847         found = v;
00848         best_dist = dist;
00849       }
00850     }
00851   }
00852 
00853   return found;
00854 }
00855 
00856 void DecreaseVehicleValue(Vehicle *v)
00857 {
00858   v->value -= v->value >> 8;
00859   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00860 }
00861 
00862 static const byte _breakdown_chance[64] = {
00863     3,   3,   3,   3,   3,   3,   3,   3,
00864     4,   4,   5,   5,   6,   6,   7,   7,
00865     8,   8,   9,   9,  10,  10,  11,  11,
00866    12,  13,  13,  13,  13,  14,  15,  16,
00867    17,  19,  21,  25,  28,  31,  34,  37,
00868    40,  44,  48,  52,  56,  60,  64,  68,
00869    72,  80,  90, 100, 110, 120, 130, 140,
00870   150, 170, 190, 210, 230, 250, 250, 250,
00871 };
00872 
00873 void CheckVehicleBreakdown(Vehicle *v)
00874 {
00875   int rel, rel_old;
00876 
00877   /* decrease reliability */
00878   v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
00879   if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00880 
00881   if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
00882       _settings_game.difficulty.vehicle_breakdowns < 1 ||
00883       v->cur_speed < 5 || _game_mode == GM_MENU) {
00884     return;
00885   }
00886 
00887   uint32 r = Random();
00888 
00889   /* increase chance of failure */
00890   int chance = v->breakdown_chance + 1;
00891   if (Chance16I(1, 25, r)) chance += 25;
00892   v->breakdown_chance = min(255, chance);
00893 
00894   /* calculate reliability value to use in comparison */
00895   rel = v->reliability;
00896   if (v->type == VEH_SHIP) rel += 0x6666;
00897 
00898   /* reduced breakdowns? */
00899   if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
00900 
00901   /* check if to break down */
00902   if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
00903     v->breakdown_ctr    = GB(r, 16, 6) + 0x3F;
00904     v->breakdown_delay  = GB(r, 24, 7) + 0x80;
00905     v->breakdown_chance = 0;
00906   }
00907 }
00908 
00909 void AgeVehicle(Vehicle *v)
00910 {
00911   if (v->age < 65535) v->age++;
00912 
00913   int age = v->age - v->max_age;
00914   if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
00915       age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
00916     v->reliability_spd_dec <<= 1;
00917   }
00918 
00919   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00920 
00921   /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
00922   if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
00923 
00924   /* Don't warn if a renew is active */
00925   if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
00926 
00927   StringID str;
00928   if (age == -DAYS_IN_LEAP_YEAR) {
00929     str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
00930   } else if (age == 0) {
00931     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
00932   } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
00933     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
00934   } else {
00935     return;
00936   }
00937 
00938   SetDParam(0, v->index);
00939   AddVehicleNewsItem(str, NS_ADVICE, v->index);
00940 }
00941 
00948 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
00949 {
00950   int count = 0;
00951   int max = 0;
00952   int cars = 0;
00953   int unloading = 0;
00954   bool loading = false;
00955 
00956   const Vehicle *u = v;
00957   const Station *st = v->last_station_visited != INVALID_STATION ? Station::Get(v->last_station_visited) : NULL;
00958 
00959   /* Count up max and used */
00960   for (; v != NULL; v = v->Next()) {
00961     count += v->cargo.Count();
00962     max += v->cargo_cap;
00963     if (v->cargo_cap != 0 && colour != NULL) {
00964       unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
00965       loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
00966       cars++;
00967     }
00968   }
00969 
00970   if (colour != NULL) {
00971     if (unloading == 0 && loading) {
00972       *colour = STR_PERCENT_UP;
00973     } else if (cars == unloading || !loading) {
00974       *colour = STR_PERCENT_DOWN;
00975     } else {
00976       *colour = STR_PERCENT_UP_DOWN;
00977     }
00978   }
00979 
00980   /* Train without capacity */
00981   if (max == 0) return 100;
00982 
00983   /* Return the percentage */
00984   return (count * 100) / max;
00985 }
00986 
00987 void VehicleEnterDepot(Vehicle *v)
00988 {
00989   /* Always work with the front of the vehicle */
00990   assert(v == v->First());
00991 
00992   switch (v->type) {
00993     case VEH_TRAIN: {
00994       Train *t = Train::From(v);
00995       SetWindowClassesDirty(WC_TRAINS_LIST);
00996       /* Clear path reservation */
00997       SetDepotReservation(t->tile, false);
00998       if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
00999 
01000       UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01001       t->time_counter = 0;
01002       ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01003       TrainConsistChanged(t, true);
01004       break;
01005     }
01006 
01007     case VEH_ROAD:
01008       SetWindowClassesDirty(WC_ROADVEH_LIST);
01009       break;
01010 
01011     case VEH_SHIP:
01012       SetWindowClassesDirty(WC_SHIPS_LIST);
01013       Ship::From(v)->state = TRACK_BIT_DEPOT;
01014       RecalcShipStuff(v);
01015       break;
01016 
01017     case VEH_AIRCRAFT:
01018       SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01019       HandleAircraftEnterHangar(Aircraft::From(v));
01020       break;
01021     default: NOT_REACHED();
01022   }
01023 
01024   if (v->type != VEH_TRAIN) {
01025     /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
01026      * We only increase the number of vehicles when the first one enters, so we will not need to search for more vehicles in the depot */
01027     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01028   }
01029   SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01030 
01031   v->vehstatus |= VS_HIDDEN;
01032   v->cur_speed = 0;
01033 
01034   VehicleServiceInDepot(v);
01035 
01036   TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01037 
01038   if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01039     SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01040 
01041     const Order *real_order = v->GetOrder(v->cur_order_index);
01042     Order t = v->current_order;
01043     v->current_order.MakeDummy();
01044 
01045     /* Test whether we are heading for this depot. If not, do nothing.
01046      * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
01047     if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01048         real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01049         (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01050       /* We are heading for another depot, keep driving. */
01051       return;
01052     }
01053 
01054     if (t.IsRefit()) {
01055       _current_company = v->owner;
01056       CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01057 
01058       if (CmdFailed(cost)) {
01059         _vehicles_to_autoreplace[v] = false;
01060         if (v->owner == _local_company) {
01061           /* Notify the user that we stopped the vehicle */
01062           SetDParam(0, v->index);
01063           AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01064         }
01065       } else if (v->owner == _local_company && cost.GetCost() != 0) {
01066         ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01067       }
01068     }
01069 
01070     if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01071       /* Part of orders */
01072       UpdateVehicleTimetable(v, true);
01073       v->IncrementOrderIndex();
01074     }
01075     if (t.GetDepotActionType() & ODATFB_HALT) {
01076       /* Vehicles are always stopped on entering depots. Do not restart this one. */
01077       _vehicles_to_autoreplace[v] = false;
01078       if (v->owner == _local_company) {
01079         SetDParam(0, v->index);
01080         AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01081       }
01082       AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01083     }
01084   }
01085 }
01086 
01087 
01095 void VehicleMove(Vehicle *v, bool update_viewport)
01096 {
01097   int img = v->cur_image;
01098   Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01099   const Sprite *spr = GetSprite(img, ST_NORMAL);
01100 
01101   pt.x += spr->x_offs;
01102   pt.y += spr->y_offs;
01103 
01104   UpdateVehiclePosHash(v, pt.x, pt.y);
01105 
01106   Rect old_coord = v->coord;
01107   v->coord.left   = pt.x;
01108   v->coord.top    = pt.y;
01109   v->coord.right  = pt.x + spr->width + 2;
01110   v->coord.bottom = pt.y + spr->height + 2;
01111 
01112   if (update_viewport) {
01113     MarkAllViewportsDirty(
01114       min(old_coord.left,   v->coord.left),
01115       min(old_coord.top,    v->coord.top),
01116       max(old_coord.right,  v->coord.right) + 1,
01117       max(old_coord.bottom, v->coord.bottom) + 1
01118     );
01119   }
01120 }
01121 
01130 void MarkSingleVehicleDirty(const Vehicle *v)
01131 {
01132   MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01133 }
01134 
01139 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01140 {
01141   static const int8 _delta_coord[16] = {
01142     -1,-1,-1, 0, 1, 1, 1, 0, /* x */
01143     -1, 0, 1, 1, 1, 0,-1,-1, /* y */
01144   };
01145 
01146   int x = v->x_pos + _delta_coord[v->direction];
01147   int y = v->y_pos + _delta_coord[v->direction + 8];
01148 
01149   GetNewVehiclePosResult gp;
01150   gp.x = x;
01151   gp.y = y;
01152   gp.old_tile = v->tile;
01153   gp.new_tile = TileVirtXY(x, y);
01154   return gp;
01155 }
01156 
01157 static const Direction _new_direction_table[] = {
01158   DIR_N,  DIR_NW, DIR_W,
01159   DIR_NE, DIR_SE, DIR_SW,
01160   DIR_E,  DIR_SE, DIR_S
01161 };
01162 
01163 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01164 {
01165   int i = 0;
01166 
01167   if (y >= v->y_pos) {
01168     if (y != v->y_pos) i += 3;
01169     i += 3;
01170   }
01171 
01172   if (x >= v->x_pos) {
01173     if (x != v->x_pos) i++;
01174     i++;
01175   }
01176 
01177   Direction dir = v->direction;
01178 
01179   DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01180   if (dirdiff == DIRDIFF_SAME) return dir;
01181   return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01182 }
01183 
01193 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01194 {
01195   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01196 }
01197 
01198 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01199 {
01200   /* Find maximum */
01201   const Vehicle *v;
01202   FOR_ALL_VEHICLES(v) {
01203     if (v->type == type && v->owner == owner) {
01204       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01205     }
01206   }
01207 
01208   if (this->maxid == 0) return;
01209 
01210   this->maxid++; // so there is space for last item (with v->unitnumber == maxid)
01211   this->maxid++; // this one will always be free (well, it will fail when there are 65535 units, so this overflows)
01212 
01213   this->cache = CallocT<bool>(this->maxid);
01214 
01215   /* Fill the cache */
01216   FOR_ALL_VEHICLES(v) {
01217     if (v->type == type && v->owner == owner) {
01218       this->cache[v->unitnumber] = true;
01219     }
01220   }
01221 }
01222 
01223 UnitID FreeUnitIDGenerator::NextID()
01224 {
01225   if (this->maxid <= this->curid) return ++this->curid;
01226 
01227   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
01228 
01229   return this->curid;
01230 }
01231 
01232 UnitID GetFreeUnitNumber(VehicleType type)
01233 {
01234   FreeUnitIDGenerator gen(type, _current_company);
01235 
01236   return gen.NextID();
01237 }
01238 
01239 
01248 bool CanBuildVehicleInfrastructure(VehicleType type)
01249 {
01250   assert(IsCompanyBuildableVehicleType(type));
01251 
01252   if (!Company::IsValidID(_local_company)) return false;
01253   if (_settings_client.gui.always_build_infrastructure) return true;
01254 
01255   UnitID max;
01256   switch (type) {
01257     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
01258     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
01259     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
01260     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01261     default: NOT_REACHED();
01262   }
01263 
01264   /* We can build vehicle infrastructure when we may build the vehicle type */
01265   if (max > 0) {
01266     /* Can we actually build the vehicle type? */
01267     const Engine *e;
01268     FOR_ALL_ENGINES_OF_TYPE(e, type) {
01269       if (HasBit(e->company_avail, _local_company)) return true;
01270     }
01271     return false;
01272   }
01273 
01274   /* We should be able to build infrastructure when we have the actual vehicle type */
01275   const Vehicle *v;
01276   FOR_ALL_VEHICLES(v) {
01277     if (v->owner == _local_company && v->type == type) return true;
01278   }
01279 
01280   return false;
01281 }
01282 
01283 
01292 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01293 {
01294   const Company *c = Company::Get(company);
01295   LiveryScheme scheme = LS_DEFAULT;
01296   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01297 
01298   /* The default livery is always available for use, but its in_use flag determines
01299    * whether any _other_ liveries are in use. */
01300   if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
01301     /* Determine the livery scheme to use */
01302     const Engine *e = Engine::Get(engine_type);
01303     switch (e->type) {
01304       default: NOT_REACHED();
01305       case VEH_TRAIN: {
01306         if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (Train::From(v)->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01307           /* Wagonoverrides use the coloir scheme of the front engine.
01308            * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
01309           engine_type = parent_engine_type;
01310           e = Engine::Get(engine_type);
01311           /* Note: Luckily cargo_type is not needed for engines */
01312         }
01313 
01314         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01315         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01316         if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01317           if (!CargoSpec::Get(cargo_type)->is_freight) {
01318             if (parent_engine_type == INVALID_ENGINE) {
01319               scheme = LS_PASSENGER_WAGON_STEAM;
01320             } else {
01321               switch (RailVehInfo(parent_engine_type)->engclass) {
01322                 default: NOT_REACHED();
01323                 case EC_STEAM:    scheme = LS_PASSENGER_WAGON_STEAM;    break;
01324                 case EC_DIESEL:   scheme = LS_PASSENGER_WAGON_DIESEL;   break;
01325                 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
01326                 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
01327                 case EC_MAGLEV:   scheme = LS_PASSENGER_WAGON_MAGLEV;   break;
01328               }
01329             }
01330           } else {
01331             scheme = LS_FREIGHT_WAGON;
01332           }
01333         } else {
01334           bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01335 
01336           switch (e->u.rail.engclass) {
01337             default: NOT_REACHED();
01338             case EC_STEAM:    scheme = LS_STEAM; break;
01339             case EC_DIESEL:   scheme = is_mu ? LS_DMU : LS_DIESEL;   break;
01340             case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
01341             case EC_MONORAIL: scheme = LS_MONORAIL; break;
01342             case EC_MAGLEV:   scheme = LS_MAGLEV; break;
01343           }
01344         }
01345         break;
01346       }
01347 
01348       case VEH_ROAD: {
01349         /* Always use the livery of the front */
01350         if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01351           engine_type = parent_engine_type;
01352           e = Engine::Get(engine_type);
01353           cargo_type = v->First()->cargo_type;
01354         }
01355         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01356         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01357 
01358         /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
01359         if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01360           /* Tram */
01361           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01362         } else {
01363           /* Bus or truck */
01364           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01365         }
01366         break;
01367       }
01368 
01369       case VEH_SHIP: {
01370         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01371         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01372         scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01373         break;
01374       }
01375 
01376       case VEH_AIRCRAFT: {
01377         switch (e->u.air.subtype) {
01378           case AIR_HELI: scheme = LS_HELICOPTER; break;
01379           case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
01380           case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
01381         }
01382         break;
01383       }
01384     }
01385 
01386     /* Switch back to the default scheme if the resolved scheme is not in use */
01387     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01388   }
01389 
01390   return &c->livery[scheme];
01391 }
01392 
01393 
01394 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01395 {
01396   SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01397 
01398   /* Return cached value if any */
01399   if (map != PAL_NONE) return map;
01400 
01401   const Engine *e = Engine::Get(engine_type);
01402 
01403   /* Check if we should use the colour map callback */
01404   if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01405     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01406     /* A return value of 0xC000 is stated to "use the default two-colour
01407      * maps" which happens to be the failure action too... */
01408     if (callback != CALLBACK_FAILED && callback != 0xC000) {
01409       map = GB(callback, 0, 14);
01410       /* If bit 14 is set, then the company colours are applied to the
01411        * map else it's returned as-is. */
01412       if (!HasBit(callback, 14)) {
01413         /* Update cache */
01414         if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01415         return map;
01416       }
01417     }
01418   }
01419 
01420   bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01421 
01422   if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
01423 
01424   /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
01425   if (!Company::IsValidID(company)) return map;
01426 
01427   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
01428 
01429   map += livery->colour1;
01430   if (twocc) map += livery->colour2 * 16;
01431 
01432   /* Update cache */
01433   if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01434   return map;
01435 }
01436 
01437 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
01438 {
01439   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01440 }
01441 
01442 SpriteID GetVehiclePalette(const Vehicle *v)
01443 {
01444   if (v->type == VEH_TRAIN) {
01445     return GetEngineColourMap(v->engine_type, v->owner, Train::From(v)->tcache.first_engine, v);
01446   } else if (v->type == VEH_ROAD) {
01447     return GetEngineColourMap(v->engine_type, v->owner, RoadVehicle::From(v)->rcache.first_engine, v);
01448   }
01449 
01450   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01451 }
01452 
01461 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01462 {
01463   if (mail_capacity != NULL) *mail_capacity = 0;
01464   const Engine *e = Engine::Get(v->engine_type);
01465 
01466   if (!e->CanCarryCargo()) return 0;
01467 
01468   if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01469     *mail_capacity = e->u.air.mail_capacity;
01470   }
01471   CargoID default_cargo = e->GetDefaultCargoType();
01472 
01473   /* Check the refit capacity callback if we are not in the default configuration.
01474    * Note: This might change to become more consistent/flexible/sane, esp. when default cargo is first refittable. */
01475   if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01476       (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01477     uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01478     if (callback != CALLBACK_FAILED) return callback;
01479   }
01480 
01481   /* Get capacity according to property resp. CB */
01482   uint capacity;
01483   switch (e->type) {
01484     case VEH_TRAIN:    capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY,   e->u.rail.capacity); break;
01485     case VEH_ROAD:     capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01486     case VEH_SHIP:     capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY,    e->u.ship.capacity); break;
01487     case VEH_AIRCRAFT: capacity = e->u.air.passenger_capacity; break;
01488     default: NOT_REACHED();
01489   }
01490 
01491   /* Apply multipliers depending on cargo- and vehicletype.
01492    * Note: This might change to become more consistent/flexible. */
01493   if (e->type != VEH_SHIP) {
01494     if (e->type == VEH_AIRCRAFT) {
01495       if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01496         capacity += e->u.air.mail_capacity;
01497       }
01498       if (v->cargo_type == CT_MAIL) return capacity;
01499     } else {
01500       switch (default_cargo) {
01501         case CT_PASSENGERS: break;
01502         case CT_MAIL:
01503         case CT_GOODS: capacity *= 2; break;
01504         default:       capacity *= 4; break;
01505       }
01506     }
01507     switch (v->cargo_type) {
01508       case CT_PASSENGERS: break;
01509       case CT_MAIL:
01510       case CT_GOODS: capacity /= 2; break;
01511       default:       capacity /= 4; break;
01512     }
01513   }
01514 
01515   return capacity;
01516 }
01517 
01518 
01519 void Vehicle::BeginLoading()
01520 {
01521   assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01522 
01523   if (this->current_order.IsType(OT_GOTO_STATION) &&
01524       this->current_order.GetDestination() == this->last_station_visited) {
01525     current_order.MakeLoading(true);
01526     UpdateVehicleTimetable(this, true);
01527 
01528     /* Furthermore add the Non Stop flag to mark that this station
01529      * is the actual destination of the vehicle, which is (for example)
01530      * necessary to be known for HandleTrainLoading to determine
01531      * whether the train is lost or not; not marking a train lost
01532      * that arrives at random stations is bad. */
01533     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01534 
01535   } else {
01536     current_order.MakeLoading(false);
01537   }
01538 
01539   Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01540 
01541   PrepareUnload(this);
01542 
01543   SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01544   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01545   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01546   SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01547 
01548   Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01549   this->cur_speed = 0;
01550   this->MarkDirty();
01551 }
01552 
01553 void Vehicle::LeaveStation()
01554 {
01555   assert(current_order.IsType(OT_LOADING));
01556 
01557   delete this->cargo_payment;
01558 
01559   /* Only update the timetable if the vehicle was supposed to stop here. */
01560   if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01561 
01562   current_order.MakeLeaveStation();
01563   Station *st = Station::Get(this->last_station_visited);
01564   st->loading_vehicles.remove(this);
01565 
01566   HideFillingPercent(&this->fill_percent_te_id);
01567 
01568   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01569     /* Trigger station animation (trains only) */
01570     if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
01571 
01572     /* Try to reserve a path when leaving the station as we
01573      * might not be marked as wanting a reservation, e.g.
01574      * when an overlength train gets turned around in a station. */
01575     if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(this->GetVehicleTrackdir()), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
01576       TryPathReserve(Train::From(this), true, true);
01577     }
01578   }
01579 }
01580 
01581 
01582 void Vehicle::HandleLoading(bool mode)
01583 {
01584   switch (this->current_order.GetType()) {
01585     case OT_LOADING: {
01586       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01587 
01588       /* Not the first call for this tick, or still loading */
01589       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01590           (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01591 
01592       this->PlayLeaveStationSound();
01593 
01594       bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
01595       this->LeaveStation();
01596 
01597       /* If this was not the final order, don't remove it from the list. */
01598       if (!at_destination_station) return;
01599       break;
01600     }
01601 
01602     case OT_DUMMY: break;
01603 
01604     default: return;
01605   }
01606 
01607   this->IncrementOrderIndex();
01608 }
01609 
01610 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01611 {
01612   if (!CheckOwnership(this->owner)) return CMD_ERROR;
01613   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01614   if (this->IsStoppedInDepot()) return CMD_ERROR;
01615 
01616   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01617     bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01618     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01619       /* We called with a different DEPOT_SERVICE setting.
01620        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
01621        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
01622       if (flags & DC_EXEC) {
01623         this->current_order.SetDepotOrderType(ODTF_MANUAL);
01624         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01625         SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01626       }
01627       return CommandCost();
01628     }
01629 
01630     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
01631     if (flags & DC_EXEC) {
01632       /* If the orders to 'goto depot' are in the orders list (forced servicing),
01633        * then skip to the next order; effectively cancelling this forced service */
01634       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
01635 
01636       this->current_order.MakeDummy();
01637       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01638     }
01639     return CommandCost();
01640   }
01641 
01642   TileIndex location;
01643   DestinationID destination;
01644   bool reverse;
01645   static const StringID no_depot[] = {STR_ERROR_UNABLE_TO_FIND_ROUTE_TO, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_CAN_T_SEND_AIRCRAFT_TO_HANGAR};
01646   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01647 
01648   if (flags & DC_EXEC) {
01649     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01650 
01651     this->dest_tile = location;
01652     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01653     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01654     SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01655 
01656     /* If there is no depot in front, reverse automatically (trains only) */
01657     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01658 
01659     if (this->type == VEH_AIRCRAFT) {
01660       Aircraft *a = Aircraft::From(this);
01661       if (a->state == FLYING && a->targetairport != destination) {
01662         /* The aircraft is now heading for a different hangar than the next in the orders */
01663         extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01664         AircraftNextAirportPos_and_Order(a);
01665       }
01666     }
01667   }
01668 
01669   return CommandCost();
01670 
01671 }
01672 
01673 void Vehicle::SetNext(Vehicle *next)
01674 {
01675   assert(this != next);
01676 
01677   if (this->next != NULL) {
01678     /* We had an old next vehicle. Update the first and previous pointers */
01679     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01680       v->first = this->next;
01681     }
01682     this->next->previous = NULL;
01683   }
01684 
01685   this->next = next;
01686 
01687   if (this->next != NULL) {
01688     /* A new next vehicle. Update the first and previous pointers */
01689     if (this->next->previous != NULL) this->next->previous->next = NULL;
01690     this->next->previous = this;
01691     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01692       v->first = this->first;
01693     }
01694   }
01695 }
01696 
01697 void Vehicle::AddToShared(Vehicle *shared_chain)
01698 {
01699   assert(this->previous_shared == NULL && this->next_shared == NULL);
01700 
01701   if (!shared_chain->orders.list) {
01702     assert(shared_chain->previous_shared == NULL);
01703     assert(shared_chain->next_shared == NULL);
01704     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
01705   }
01706 
01707   this->next_shared     = shared_chain->next_shared;
01708   this->previous_shared = shared_chain;
01709 
01710   shared_chain->next_shared = this;
01711 
01712   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
01713 
01714   shared_chain->orders.list->AddVehicle(this);
01715 }
01716 
01717 void Vehicle::RemoveFromShared()
01718 {
01719   /* Remember if we were first and the old window number before RemoveVehicle()
01720    * as this changes first if needed. */
01721   bool were_first = (this->FirstShared() == this);
01722   uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
01723 
01724   this->orders.list->RemoveVehicle(this);
01725 
01726   if (!were_first) {
01727     /* We are not the first shared one, so only relink our previous one. */
01728     this->previous_shared->next_shared = this->NextShared();
01729   }
01730 
01731   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
01732 
01733 
01734   if (this->orders.list->GetNumVehicles() == 1) {
01735     /* When there is only one vehicle, remove the shared order list window. */
01736     DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
01737     InvalidateVehicleOrder(this->FirstShared(), 0);
01738   } else if (were_first) {
01739     /* If we were the first one, update to the new first one.
01740      * Note: FirstShared() is already the new first */
01741     InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
01742   }
01743 
01744   this->next_shared     = NULL;
01745   this->previous_shared = NULL;
01746 }
01747 
01748 void StopAllVehicles()
01749 {
01750   Vehicle *v;
01751   FOR_ALL_VEHICLES(v) {
01752     /* Code ripped from CmdStartStopTrain. Can't call it, because of
01753      * ownership problems, so we'll duplicate some code, for now */
01754     v->vehstatus |= VS_STOPPED;
01755     v->MarkDirty();
01756     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01757     SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01758   }
01759 }
01760 
01761 void VehiclesYearlyLoop()
01762 {
01763   Vehicle *v;
01764   FOR_ALL_VEHICLES(v) {
01765     if (v->IsPrimaryVehicle()) {
01766       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
01767       Money profit = v->GetDisplayProfitThisYear();
01768       if (v->age >= 730 && profit < 0) {
01769         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
01770           SetDParam(0, v->index);
01771           SetDParam(1, profit);
01772           AddVehicleNewsItem(
01773             STR_NEWS_VEHICLE_IS_UNPROFITABLE,
01774             NS_ADVICE,
01775             v->index
01776           );
01777         }
01778         AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
01779       }
01780 
01781       v->profit_last_year = v->profit_this_year;
01782       v->profit_this_year = 0;
01783       SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01784     }
01785   }
01786 }
01787 
01788 
01798 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
01799 {
01800   const Engine *e = Engine::GetIfValid(engine_type);
01801   assert(e != NULL);
01802 
01803   switch (e->type) {
01804     case VEH_TRAIN:
01805       return (st->facilities & FACIL_TRAIN) != 0;
01806 
01807     case VEH_ROAD:
01808       /* For road vehicles we need the vehicle to know whether it can actually
01809        * use the station, but if it doesn't have facilities for RVs it is
01810        * certainly not possible that the station can be used. */
01811       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
01812 
01813     case VEH_SHIP:
01814       return (st->facilities & FACIL_DOCK) != 0;
01815 
01816     case VEH_AIRCRAFT:
01817       return (st->facilities & FACIL_AIRPORT) != 0 &&
01818           (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
01819 
01820     default:
01821       return false;
01822   }
01823 }
01824 
01831 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
01832 {
01833   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
01834 
01835   return CanVehicleUseStation(v->engine_type, st);
01836 }

Generated on Tue Jan 5 21:03:00 2010 for OpenTTD by  doxygen 1.5.6