vehicle.cpp

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

Generated on Sat Jul 17 18:43:26 2010 for OpenTTD by  doxygen 1.6.1