vehicle.cpp

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

Generated on Sat Dec 26 20:06:07 2009 for OpenTTD by  doxygen 1.5.6