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

Generated on Wed Dec 30 20:40:08 2009 for OpenTTD by  doxygen 1.5.6