vehicle.cpp

Go to the documentation of this file.
00001 /* $Id: vehicle.cpp 19223 2010-02-23 23:26:37Z 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       ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01005       t->ConsistChanged(true);
01006       break;
01007     }
01008 
01009     case VEH_ROAD:
01010       SetWindowClassesDirty(WC_ROADVEH_LIST);
01011       break;
01012 
01013     case VEH_SHIP:
01014       SetWindowClassesDirty(WC_SHIPS_LIST);
01015       Ship::From(v)->state = TRACK_BIT_DEPOT;
01016       RecalcShipStuff(v);
01017       break;
01018 
01019     case VEH_AIRCRAFT:
01020       SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01021       HandleAircraftEnterHangar(Aircraft::From(v));
01022       break;
01023     default: NOT_REACHED();
01024   }
01025 
01026   if (v->type != VEH_TRAIN) {
01027     /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
01028      * 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 */
01029     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01030   }
01031   SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01032 
01033   v->vehstatus |= VS_HIDDEN;
01034   v->cur_speed = 0;
01035 
01036   VehicleServiceInDepot(v);
01037 
01038   TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01039 
01040   if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01041     SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01042 
01043     const Order *real_order = v->GetOrder(v->cur_order_index);
01044     Order t = v->current_order;
01045     v->current_order.MakeDummy();
01046 
01047     /* Test whether we are heading for this depot. If not, do nothing.
01048      * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
01049     if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01050         real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01051         (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01052       /* We are heading for another depot, keep driving. */
01053       return;
01054     }
01055 
01056     if (t.IsRefit()) {
01057       _current_company = v->owner;
01058       CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01059 
01060       if (cost.Failed()) {
01061         _vehicles_to_autoreplace[v] = false;
01062         if (v->owner == _local_company) {
01063           /* Notify the user that we stopped the vehicle */
01064           SetDParam(0, v->index);
01065           AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01066         }
01067       } else if (v->owner == _local_company && cost.GetCost() != 0) {
01068         ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01069       }
01070     }
01071 
01072     if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01073       /* Part of orders */
01074       UpdateVehicleTimetable(v, true);
01075       v->IncrementOrderIndex();
01076     }
01077     if (t.GetDepotActionType() & ODATFB_HALT) {
01078       /* Vehicles are always stopped on entering depots. Do not restart this one. */
01079       _vehicles_to_autoreplace[v] = false;
01080       if (v->owner == _local_company) {
01081         SetDParam(0, v->index);
01082         AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01083       }
01084       AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01085     }
01086   }
01087 }
01088 
01089 
01097 void VehicleMove(Vehicle *v, bool update_viewport)
01098 {
01099   int img = v->cur_image;
01100   Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01101   const Sprite *spr = GetSprite(img, ST_NORMAL);
01102 
01103   pt.x += spr->x_offs;
01104   pt.y += spr->y_offs;
01105 
01106   UpdateVehiclePosHash(v, pt.x, pt.y);
01107 
01108   Rect old_coord = v->coord;
01109   v->coord.left   = pt.x;
01110   v->coord.top    = pt.y;
01111   v->coord.right  = pt.x + spr->width + 2;
01112   v->coord.bottom = pt.y + spr->height + 2;
01113 
01114   if (update_viewport) {
01115     MarkAllViewportsDirty(
01116       min(old_coord.left,   v->coord.left),
01117       min(old_coord.top,    v->coord.top),
01118       max(old_coord.right,  v->coord.right) + 1,
01119       max(old_coord.bottom, v->coord.bottom) + 1
01120     );
01121   }
01122 }
01123 
01132 void MarkSingleVehicleDirty(const Vehicle *v)
01133 {
01134   MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01135 }
01136 
01141 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01142 {
01143   static const int8 _delta_coord[16] = {
01144     -1,-1,-1, 0, 1, 1, 1, 0, /* x */
01145     -1, 0, 1, 1, 1, 0,-1,-1, /* y */
01146   };
01147 
01148   int x = v->x_pos + _delta_coord[v->direction];
01149   int y = v->y_pos + _delta_coord[v->direction + 8];
01150 
01151   GetNewVehiclePosResult gp;
01152   gp.x = x;
01153   gp.y = y;
01154   gp.old_tile = v->tile;
01155   gp.new_tile = TileVirtXY(x, y);
01156   return gp;
01157 }
01158 
01159 static const Direction _new_direction_table[] = {
01160   DIR_N,  DIR_NW, DIR_W,
01161   DIR_NE, DIR_SE, DIR_SW,
01162   DIR_E,  DIR_SE, DIR_S
01163 };
01164 
01165 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01166 {
01167   int i = 0;
01168 
01169   if (y >= v->y_pos) {
01170     if (y != v->y_pos) i += 3;
01171     i += 3;
01172   }
01173 
01174   if (x >= v->x_pos) {
01175     if (x != v->x_pos) i++;
01176     i++;
01177   }
01178 
01179   Direction dir = v->direction;
01180 
01181   DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01182   if (dirdiff == DIRDIFF_SAME) return dir;
01183   return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01184 }
01185 
01195 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01196 {
01197   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01198 }
01199 
01200 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01201 {
01202   /* Find maximum */
01203   const Vehicle *v;
01204   FOR_ALL_VEHICLES(v) {
01205     if (v->type == type && v->owner == owner) {
01206       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01207     }
01208   }
01209 
01210   if (this->maxid == 0) return;
01211 
01212   /* Reserving 'maxid + 2' because we need:
01213    * - space for the last item (with v->unitnumber == maxid)
01214    * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
01215   this->cache = CallocT<bool>(this->maxid + 2);
01216 
01217   /* Fill the cache */
01218   FOR_ALL_VEHICLES(v) {
01219     if (v->type == type && v->owner == owner) {
01220       this->cache[v->unitnumber] = true;
01221     }
01222   }
01223 }
01224 
01225 UnitID FreeUnitIDGenerator::NextID()
01226 {
01227   if (this->maxid <= this->curid) return ++this->curid;
01228 
01229   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
01230 
01231   return this->curid;
01232 }
01233 
01234 UnitID GetFreeUnitNumber(VehicleType type)
01235 {
01236   FreeUnitIDGenerator gen(type, _current_company);
01237 
01238   return gen.NextID();
01239 }
01240 
01241 
01250 bool CanBuildVehicleInfrastructure(VehicleType type)
01251 {
01252   assert(IsCompanyBuildableVehicleType(type));
01253 
01254   if (!Company::IsValidID(_local_company)) return false;
01255   if (_settings_client.gui.always_build_infrastructure) return true;
01256 
01257   UnitID max;
01258   switch (type) {
01259     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
01260     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
01261     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
01262     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01263     default: NOT_REACHED();
01264   }
01265 
01266   /* We can build vehicle infrastructure when we may build the vehicle type */
01267   if (max > 0) {
01268     /* Can we actually build the vehicle type? */
01269     const Engine *e;
01270     FOR_ALL_ENGINES_OF_TYPE(e, type) {
01271       if (HasBit(e->company_avail, _local_company)) return true;
01272     }
01273     return false;
01274   }
01275 
01276   /* We should be able to build infrastructure when we have the actual vehicle type */
01277   const Vehicle *v;
01278   FOR_ALL_VEHICLES(v) {
01279     if (v->owner == _local_company && v->type == type) return true;
01280   }
01281 
01282   return false;
01283 }
01284 
01285 
01294 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01295 {
01296   const Company *c = Company::Get(company);
01297   LiveryScheme scheme = LS_DEFAULT;
01298   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01299 
01300   /* The default livery is always available for use, but its in_use flag determines
01301    * whether any _other_ liveries are in use. */
01302   if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
01303     /* Determine the livery scheme to use */
01304     const Engine *e = Engine::Get(engine_type);
01305     switch (e->type) {
01306       default: NOT_REACHED();
01307       case VEH_TRAIN: {
01308         if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (Train::From(v)->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01309           /* Wagonoverrides use the coloir scheme of the front engine.
01310            * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
01311           engine_type = parent_engine_type;
01312           e = Engine::Get(engine_type);
01313           /* Note: Luckily cargo_type is not needed for engines */
01314         }
01315 
01316         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01317         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01318         if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01319           if (!CargoSpec::Get(cargo_type)->is_freight) {
01320             if (parent_engine_type == INVALID_ENGINE) {
01321               scheme = LS_PASSENGER_WAGON_STEAM;
01322             } else {
01323               switch (RailVehInfo(parent_engine_type)->engclass) {
01324                 default: NOT_REACHED();
01325                 case EC_STEAM:    scheme = LS_PASSENGER_WAGON_STEAM;    break;
01326                 case EC_DIESEL:   scheme = LS_PASSENGER_WAGON_DIESEL;   break;
01327                 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
01328                 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
01329                 case EC_MAGLEV:   scheme = LS_PASSENGER_WAGON_MAGLEV;   break;
01330               }
01331             }
01332           } else {
01333             scheme = LS_FREIGHT_WAGON;
01334           }
01335         } else {
01336           bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01337 
01338           switch (e->u.rail.engclass) {
01339             default: NOT_REACHED();
01340             case EC_STEAM:    scheme = LS_STEAM; break;
01341             case EC_DIESEL:   scheme = is_mu ? LS_DMU : LS_DIESEL;   break;
01342             case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
01343             case EC_MONORAIL: scheme = LS_MONORAIL; break;
01344             case EC_MAGLEV:   scheme = LS_MAGLEV; break;
01345           }
01346         }
01347         break;
01348       }
01349 
01350       case VEH_ROAD: {
01351         /* Always use the livery of the front */
01352         if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01353           engine_type = parent_engine_type;
01354           e = Engine::Get(engine_type);
01355           cargo_type = v->First()->cargo_type;
01356         }
01357         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01358         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01359 
01360         /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
01361         if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01362           /* Tram */
01363           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01364         } else {
01365           /* Bus or truck */
01366           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01367         }
01368         break;
01369       }
01370 
01371       case VEH_SHIP: {
01372         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01373         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01374         scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01375         break;
01376       }
01377 
01378       case VEH_AIRCRAFT: {
01379         switch (e->u.air.subtype) {
01380           case AIR_HELI: scheme = LS_HELICOPTER; break;
01381           case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
01382           case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
01383         }
01384         break;
01385       }
01386     }
01387 
01388     /* Switch back to the default scheme if the resolved scheme is not in use */
01389     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01390   }
01391 
01392   return &c->livery[scheme];
01393 }
01394 
01395 
01396 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01397 {
01398   PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01399 
01400   /* Return cached value if any */
01401   if (map != PAL_NONE) return map;
01402 
01403   const Engine *e = Engine::Get(engine_type);
01404 
01405   /* Check if we should use the colour map callback */
01406   if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01407     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01408     /* A return value of 0xC000 is stated to "use the default two-colour
01409      * maps" which happens to be the failure action too... */
01410     if (callback != CALLBACK_FAILED && callback != 0xC000) {
01411       map = GB(callback, 0, 14);
01412       /* If bit 14 is set, then the company colours are applied to the
01413        * map else it's returned as-is. */
01414       if (!HasBit(callback, 14)) {
01415         /* Update cache */
01416         if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01417         return map;
01418       }
01419     }
01420   }
01421 
01422   bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01423 
01424   if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01425 
01426   /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
01427   if (!Company::IsValidID(company)) return map;
01428 
01429   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
01430 
01431   map += livery->colour1;
01432   if (twocc) map += livery->colour2 * 16;
01433 
01434   /* Update cache */
01435   if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01436   return map;
01437 }
01438 
01439 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01440 {
01441   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01442 }
01443 
01444 PaletteID GetVehiclePalette(const Vehicle *v)
01445 {
01446   if (v->type == VEH_TRAIN) {
01447     return GetEngineColourMap(v->engine_type, v->owner, Train::From(v)->tcache.first_engine, v);
01448   } else if (v->type == VEH_ROAD) {
01449     return GetEngineColourMap(v->engine_type, v->owner, RoadVehicle::From(v)->rcache.first_engine, v);
01450   }
01451 
01452   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01453 }
01454 
01463 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01464 {
01465   if (mail_capacity != NULL) *mail_capacity = 0;
01466   const Engine *e = Engine::Get(v->engine_type);
01467 
01468   if (!e->CanCarryCargo()) return 0;
01469 
01470   if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01471     *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01472   }
01473   CargoID default_cargo = e->GetDefaultCargoType();
01474 
01475   /* Check the refit capacity callback if we are not in the default configuration.
01476    * Note: This might change to become more consistent/flexible/sane, esp. when default cargo is first refittable. */
01477   if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01478       (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01479     uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01480     if (callback != CALLBACK_FAILED) return callback;
01481   }
01482 
01483   /* Get capacity according to property resp. CB */
01484   uint capacity;
01485   switch (e->type) {
01486     case VEH_TRAIN:    capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY,        e->u.rail.capacity); break;
01487     case VEH_ROAD:     capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY,      e->u.road.capacity); break;
01488     case VEH_SHIP:     capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY,         e->u.ship.capacity); break;
01489     case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01490     default: NOT_REACHED();
01491   }
01492 
01493   /* Apply multipliers depending on cargo- and vehicletype.
01494    * Note: This might change to become more consistent/flexible. */
01495   if (e->type != VEH_SHIP) {
01496     if (e->type == VEH_AIRCRAFT) {
01497       if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01498         capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01499       }
01500       if (v->cargo_type == CT_MAIL) return capacity;
01501     } else {
01502       switch (default_cargo) {
01503         case CT_PASSENGERS: break;
01504         case CT_MAIL:
01505         case CT_GOODS: capacity *= 2; break;
01506         default:       capacity *= 4; break;
01507       }
01508     }
01509     switch (v->cargo_type) {
01510       case CT_PASSENGERS: break;
01511       case CT_MAIL:
01512       case CT_GOODS: capacity /= 2; break;
01513       default:       capacity /= 4; break;
01514     }
01515   }
01516 
01517   return capacity;
01518 }
01519 
01520 
01521 void Vehicle::BeginLoading()
01522 {
01523   assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01524 
01525   if (this->current_order.IsType(OT_GOTO_STATION) &&
01526       this->current_order.GetDestination() == this->last_station_visited) {
01527     current_order.MakeLoading(true);
01528     UpdateVehicleTimetable(this, true);
01529 
01530     /* Furthermore add the Non Stop flag to mark that this station
01531      * is the actual destination of the vehicle, which is (for example)
01532      * necessary to be known for HandleTrainLoading to determine
01533      * whether the train is lost or not; not marking a train lost
01534      * that arrives at random stations is bad. */
01535     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01536 
01537   } else {
01538     current_order.MakeLoading(false);
01539   }
01540 
01541   Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01542 
01543   PrepareUnload(this);
01544 
01545   SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01546   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01547   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01548   SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01549 
01550   Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01551   this->cur_speed = 0;
01552   this->MarkDirty();
01553 }
01554 
01555 void Vehicle::LeaveStation()
01556 {
01557   assert(current_order.IsType(OT_LOADING));
01558 
01559   delete this->cargo_payment;
01560 
01561   /* Only update the timetable if the vehicle was supposed to stop here. */
01562   if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01563 
01564   current_order.MakeLeaveStation();
01565   Station *st = Station::Get(this->last_station_visited);
01566   st->loading_vehicles.remove(this);
01567 
01568   HideFillingPercent(&this->fill_percent_te_id);
01569 
01570   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01571     /* Trigger station animation (trains only) */
01572     if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
01573 
01574     /* Try to reserve a path when leaving the station as we
01575      * might not be marked as wanting a reservation, e.g.
01576      * when an overlength train gets turned around in a station. */
01577     if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(this->GetVehicleTrackdir()), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
01578       TryPathReserve(Train::From(this), true, true);
01579     }
01580   }
01581 }
01582 
01583 
01584 void Vehicle::HandleLoading(bool mode)
01585 {
01586   switch (this->current_order.GetType()) {
01587     case OT_LOADING: {
01588       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01589 
01590       /* Not the first call for this tick, or still loading */
01591       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01592           (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01593 
01594       this->PlayLeaveStationSound();
01595 
01596       bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
01597       this->LeaveStation();
01598 
01599       /* If this was not the final order, don't remove it from the list. */
01600       if (!at_destination_station) return;
01601       break;
01602     }
01603 
01604     case OT_DUMMY: break;
01605 
01606     default: return;
01607   }
01608 
01609   this->IncrementOrderIndex();
01610 }
01611 
01612 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01613 {
01614   if (!CheckOwnership(this->owner)) return CMD_ERROR;
01615   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01616   if (this->IsStoppedInDepot()) return CMD_ERROR;
01617 
01618   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01619     bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01620     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01621       /* We called with a different DEPOT_SERVICE setting.
01622        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
01623        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
01624       if (flags & DC_EXEC) {
01625         this->current_order.SetDepotOrderType(ODTF_MANUAL);
01626         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01627         SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01628       }
01629       return CommandCost();
01630     }
01631 
01632     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
01633     if (flags & DC_EXEC) {
01634       /* If the orders to 'goto depot' are in the orders list (forced servicing),
01635        * then skip to the next order; effectively cancelling this forced service */
01636       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
01637 
01638       this->current_order.MakeDummy();
01639       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01640     }
01641     return CommandCost();
01642   }
01643 
01644   TileIndex location;
01645   DestinationID destination;
01646   bool reverse;
01647   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};
01648   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01649 
01650   if (flags & DC_EXEC) {
01651     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01652 
01653     this->dest_tile = location;
01654     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01655     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01656     SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01657 
01658     /* If there is no depot in front, reverse automatically (trains only) */
01659     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01660 
01661     if (this->type == VEH_AIRCRAFT) {
01662       Aircraft *a = Aircraft::From(this);
01663       if (a->state == FLYING && a->targetairport != destination) {
01664         /* The aircraft is now heading for a different hangar than the next in the orders */
01665         extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01666         AircraftNextAirportPos_and_Order(a);
01667       }
01668     }
01669   }
01670 
01671   return CommandCost();
01672 
01673 }
01674 
01675 void Vehicle::SetNext(Vehicle *next)
01676 {
01677   assert(this != next);
01678 
01679   if (this->next != NULL) {
01680     /* We had an old next vehicle. Update the first and previous pointers */
01681     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01682       v->first = this->next;
01683     }
01684     this->next->previous = NULL;
01685   }
01686 
01687   this->next = next;
01688 
01689   if (this->next != NULL) {
01690     /* A new next vehicle. Update the first and previous pointers */
01691     if (this->next->previous != NULL) this->next->previous->next = NULL;
01692     this->next->previous = this;
01693     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01694       v->first = this->first;
01695     }
01696   }
01697 }
01698 
01699 void Vehicle::AddToShared(Vehicle *shared_chain)
01700 {
01701   assert(this->previous_shared == NULL && this->next_shared == NULL);
01702 
01703   if (!shared_chain->orders.list) {
01704     assert(shared_chain->previous_shared == NULL);
01705     assert(shared_chain->next_shared == NULL);
01706     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
01707   }
01708 
01709   this->next_shared     = shared_chain->next_shared;
01710   this->previous_shared = shared_chain;
01711 
01712   shared_chain->next_shared = this;
01713 
01714   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
01715 
01716   shared_chain->orders.list->AddVehicle(this);
01717 }
01718 
01719 void Vehicle::RemoveFromShared()
01720 {
01721   /* Remember if we were first and the old window number before RemoveVehicle()
01722    * as this changes first if needed. */
01723   bool were_first = (this->FirstShared() == this);
01724   uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
01725 
01726   this->orders.list->RemoveVehicle(this);
01727 
01728   if (!were_first) {
01729     /* We are not the first shared one, so only relink our previous one. */
01730     this->previous_shared->next_shared = this->NextShared();
01731   }
01732 
01733   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
01734 
01735 
01736   if (this->orders.list->GetNumVehicles() == 1) {
01737     /* When there is only one vehicle, remove the shared order list window. */
01738     DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
01739     InvalidateVehicleOrder(this->FirstShared(), 0);
01740   } else if (were_first) {
01741     /* If we were the first one, update to the new first one.
01742      * Note: FirstShared() is already the new first */
01743     InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
01744   }
01745 
01746   this->next_shared     = NULL;
01747   this->previous_shared = NULL;
01748 }
01749 
01750 void StopAllVehicles()
01751 {
01752   Vehicle *v;
01753   FOR_ALL_VEHICLES(v) {
01754     /* Code ripped from CmdStartStopTrain. Can't call it, because of
01755      * ownership problems, so we'll duplicate some code, for now */
01756     v->vehstatus |= VS_STOPPED;
01757     v->MarkDirty();
01758     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01759     SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01760   }
01761 }
01762 
01763 void VehiclesYearlyLoop()
01764 {
01765   Vehicle *v;
01766   FOR_ALL_VEHICLES(v) {
01767     if (v->IsPrimaryVehicle()) {
01768       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
01769       Money profit = v->GetDisplayProfitThisYear();
01770       if (v->age >= 730 && profit < 0) {
01771         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
01772           SetDParam(0, v->index);
01773           SetDParam(1, profit);
01774           AddVehicleNewsItem(
01775             STR_NEWS_VEHICLE_IS_UNPROFITABLE,
01776             NS_ADVICE,
01777             v->index
01778           );
01779         }
01780         AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
01781       }
01782 
01783       v->profit_last_year = v->profit_this_year;
01784       v->profit_this_year = 0;
01785       SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01786     }
01787   }
01788 }
01789 
01790 
01800 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
01801 {
01802   const Engine *e = Engine::GetIfValid(engine_type);
01803   assert(e != NULL);
01804 
01805   switch (e->type) {
01806     case VEH_TRAIN:
01807       return (st->facilities & FACIL_TRAIN) != 0;
01808 
01809     case VEH_ROAD:
01810       /* For road vehicles we need the vehicle to know whether it can actually
01811        * use the station, but if it doesn't have facilities for RVs it is
01812        * certainly not possible that the station can be used. */
01813       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
01814 
01815     case VEH_SHIP:
01816       return (st->facilities & FACIL_DOCK) != 0;
01817 
01818     case VEH_AIRCRAFT:
01819       return (st->facilities & FACIL_AIRPORT) != 0 &&
01820           (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
01821 
01822     default:
01823       return false;
01824   }
01825 }
01826 
01833 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
01834 {
01835   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
01836 
01837   return CanVehicleUseStation(v->engine_type, st);
01838 }

Generated on Wed Mar 31 22:43:31 2010 for OpenTTD by  doxygen 1.6.1