vehicle.cpp

Go to the documentation of this file.
00001 /* $Id: vehicle.cpp 24231 2012-05-12 07:46:05Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "error.h"
00014 #include "roadveh.h"
00015 #include "ship.h"
00016 #include "spritecache.h"
00017 #include "timetable.h"
00018 #include "viewport_func.h"
00019 #include "news_func.h"
00020 #include "command_func.h"
00021 #include "company_func.h"
00022 #include "train.h"
00023 #include "aircraft.h"
00024 #include "newgrf_debug.h"
00025 #include "newgrf_sound.h"
00026 #include "newgrf_station.h"
00027 #include "group_gui.h"
00028 #include "strings_func.h"
00029 #include "zoom_func.h"
00030 #include "date_func.h"
00031 #include "vehicle_func.h"
00032 #include "autoreplace_func.h"
00033 #include "autoreplace_gui.h"
00034 #include "station_base.h"
00035 #include "ai/ai.hpp"
00036 #include "depot_func.h"
00037 #include "network/network.h"
00038 #include "core/pool_func.hpp"
00039 #include "economy_base.h"
00040 #include "articulated_vehicles.h"
00041 #include "roadstop_base.h"
00042 #include "core/random_func.hpp"
00043 #include "core/backup_type.hpp"
00044 #include "order_backup.h"
00045 #include "sound_func.h"
00046 #include "effectvehicle_func.h"
00047 #include "effectvehicle_base.h"
00048 #include "vehiclelist.h"
00049 #include "bridge_map.h"
00050 #include "tunnel_map.h"
00051 #include "depot_map.h"
00052 #include "gamelog.h"
00053 
00054 #include "table/strings.h"
00055 
00056 #define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
00057 
00058 VehicleID _new_vehicle_id;
00059 uint16 _returned_refit_capacity;      
00060 uint16 _returned_mail_refit_capacity; 
00061 
00062 
00064 VehiclePool _vehicle_pool("Vehicle");
00065 INSTANTIATE_POOL_METHODS(Vehicle)
00066 
00067 
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 
00083   /* Only engines need renewing */
00084   if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
00085 
00086   return true;
00087 }
00088 
00089 void VehicleServiceInDepot(Vehicle *v)
00090 {
00091   v->date_of_last_service = _date;
00092   v->breakdowns_since_last_service = 0;
00093   v->reliability = v->GetEngine()->reliability;
00094   SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
00095 }
00096 
00103 bool Vehicle::NeedsServicing() const
00104 {
00105   /* Stopped or crashed vehicles will not move, as such making unmovable
00106    * vehicles to go for service is lame. */
00107   if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00108 
00109   /* Are we ready for the next service cycle? */
00110   const Company *c = Company::Get(this->owner);
00111   if (c->settings.vehicle.servint_ispercent ?
00112       (this->reliability >= this->GetEngine()->reliability * (100 - this->service_interval) / 100) :
00113       (this->date_of_last_service + this->service_interval >= _date)) {
00114     return false;
00115   }
00116 
00117   /* If we're servicing anyway, because we have not disabled servicing when
00118    * there are no breakdowns or we are playing with breakdowns, bail out. */
00119   if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00120       _settings_game.difficulty.vehicle_breakdowns != 0) {
00121     return true;
00122   }
00123 
00124   /* Test whether there is some pending autoreplace.
00125    * Note: We do this after the service-interval test.
00126    * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
00127   bool pending_replace = false;
00128   Money needed_money = c->settings.engine_renew_money;
00129   if (needed_money > c->money) return false;
00130 
00131   for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00132     EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00133 
00134     /* Check engine availability */
00135     if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00136 
00137     /* Check refittability */
00138     uint32 available_cargo_types, union_mask;
00139     GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00140     /* Is there anything to refit? */
00141     if (union_mask != 0) {
00142       CargoID cargo_type;
00143       /* We cannot refit to mixed cargoes in an automated way */
00144       if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
00145 
00146       /* Did the old vehicle carry anything? */
00147       if (cargo_type != CT_INVALID) {
00148         /* We can't refit the vehicle to carry the cargo we want */
00149         if (!HasBit(available_cargo_types, cargo_type)) continue;
00150       }
00151     }
00152 
00153     /* Check money.
00154      * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
00155     pending_replace = true;
00156     needed_money += 2 * Engine::Get(new_engine)->GetCost();
00157     if (needed_money > c->money) return false;
00158   }
00159 
00160   return pending_replace;
00161 }
00162 
00168 bool Vehicle::NeedsAutomaticServicing() const
00169 {
00170   if (this->HasDepotOrder()) return false;
00171   if (this->current_order.IsType(OT_LOADING)) return false;
00172   if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00173   return NeedsServicing();
00174 }
00175 
00176 uint Vehicle::Crash(bool flooded)
00177 {
00178   assert((this->vehstatus & VS_CRASHED) == 0);
00179   assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
00180 
00181   uint pass = 0;
00182   /* Stop the vehicle. */
00183   if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00184   /* crash all wagons, and count passengers */
00185   for (Vehicle *v = this; v != NULL; v = v->Next()) {
00186     if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00187     v->vehstatus |= VS_CRASHED;
00188     MarkSingleVehicleDirty(v);
00189   }
00190 
00191   /* Dirty some windows */
00192   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00193   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
00194   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00195   SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00196 
00197   delete this->cargo_payment;
00198   this->cargo_payment = NULL;
00199 
00200   return pass;
00201 }
00202 
00203 
00212 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00213 {
00214   const Engine *e = Engine::Get(engine);
00215   GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
00216 
00217   if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00218     SetBit(grfconfig->grf_bugs, bug_type);
00219     SetDParamStr(0, grfconfig->GetName());
00220     SetDParam(1, engine);
00221     ShowErrorMessage(part1, part2, WL_CRITICAL);
00222     if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00223   }
00224 
00225   /* debug output */
00226   char buffer[512];
00227 
00228   SetDParamStr(0, grfconfig->GetName());
00229   GetString(buffer, part1, lastof(buffer));
00230   DEBUG(grf, 0, "%s", buffer + 3);
00231 
00232   SetDParam(1, engine);
00233   GetString(buffer, part2, lastof(buffer));
00234   DEBUG(grf, 0, "%s", buffer + 3);
00235 }
00236 
00242 void VehicleLengthChanged(const Vehicle *u)
00243 {
00244   /* show a warning once for each engine in whole game and once for each GRF after each game load */
00245   const Engine *engine = u->GetEngine();
00246   uint32 grfid = engine->grf_prop.grffile->grfid;
00247   GRFConfig *grfconfig = GetGRFConfig(grfid);
00248   if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
00249     ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
00250   }
00251 }
00252 
00257 Vehicle::Vehicle(VehicleType type)
00258 {
00259   this->type               = type;
00260   this->coord.left         = INVALID_COORD;
00261   this->group_id           = DEFAULT_GROUP;
00262   this->fill_percent_te_id = INVALID_TE_ID;
00263   this->first              = this;
00264   this->colourmap          = PAL_NONE;
00265   this->cargo_age_counter  = 1;
00266 }
00267 
00272 byte VehicleRandomBits()
00273 {
00274   return GB(Random(), 0, 8);
00275 }
00276 
00277 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
00278  * lookup times at the expense of memory usage. */
00279 const int HASH_BITS = 7;
00280 const int HASH_SIZE = 1 << HASH_BITS;
00281 const int HASH_MASK = HASH_SIZE - 1;
00282 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00283 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00284 
00285 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
00286  * Profiling results show that 0 is fastest. */
00287 const int HASH_RES = 0;
00288 
00289 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
00290 
00291 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00292 {
00293   for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00294     for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00295       Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00296       for (; v != NULL; v = v->hash_tile_next) {
00297         Vehicle *a = proc(v, data);
00298         if (find_first && a != NULL) return a;
00299       }
00300       if (x == xu) break;
00301     }
00302     if (y == yu) break;
00303   }
00304 
00305   return NULL;
00306 }
00307 
00308 
00320 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00321 {
00322   const int COLL_DIST = 6;
00323 
00324   /* Hash area to scan is from xl,yl to xu,yu */
00325   int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00326   int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00327   int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00328   int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00329 
00330   return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
00331 }
00332 
00347 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00348 {
00349   VehicleFromPosXY(x, y, data, proc, false);
00350 }
00351 
00363 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00364 {
00365   return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00366 }
00367 
00378 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00379 {
00380   int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00381   int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00382 
00383   Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00384   for (; v != NULL; v = v->hash_tile_next) {
00385     if (v->tile != tile) continue;
00386 
00387     Vehicle *a = proc(v, data);
00388     if (find_first && a != NULL) return a;
00389   }
00390 
00391   return NULL;
00392 }
00393 
00407 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00408 {
00409   VehicleFromPos(tile, data, proc, false);
00410 }
00411 
00422 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00423 {
00424   return VehicleFromPos(tile, data, proc, true) != NULL;
00425 }
00426 
00433 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00434 {
00435   int z = *(int*)data;
00436 
00437   if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00438   if (v->z_pos > z) return NULL;
00439 
00440   return v;
00441 }
00442 
00448 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00449 {
00450   int z = GetTileMaxPixelZ(tile);
00451 
00452   /* Value v is not safe in MP games, however, it is used to generate a local
00453    * error message only (which may be different for different machines).
00454    * Such a message does not affect MP synchronisation.
00455    */
00456   Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00457   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00458   return CommandCost();
00459 }
00460 
00462 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00463 {
00464   if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00465   if (v == (const Vehicle *)data) return NULL;
00466 
00467   return v;
00468 }
00469 
00477 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00478 {
00479   /* Value v is not safe in MP games, however, it is used to generate a local
00480    * error message only (which may be different for different machines).
00481    * Such a message does not affect MP synchronisation.
00482    */
00483   Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00484   if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00485 
00486   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00487   return CommandCost();
00488 }
00489 
00490 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00491 {
00492   TrackBits rail_bits = *(TrackBits *)data;
00493 
00494   if (v->type != VEH_TRAIN) return NULL;
00495 
00496   Train *t = Train::From(v);
00497   if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00498 
00499   return v;
00500 }
00501 
00510 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00511 {
00512   /* Value v is not safe in MP games, however, it is used to generate a local
00513    * error message only (which may be different for different machines).
00514    * Such a message does not affect MP synchronisation.
00515    */
00516   Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00517   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00518   return CommandCost();
00519 }
00520 
00521 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
00522 {
00523   Vehicle **old_hash = v->hash_tile_current;
00524   Vehicle **new_hash;
00525 
00526   if (remove) {
00527     new_hash = NULL;
00528   } else {
00529     int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00530     int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00531     new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00532   }
00533 
00534   if (old_hash == new_hash) return;
00535 
00536   /* Remove from the old position in the hash table */
00537   if (old_hash != NULL) {
00538     if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
00539     *v->hash_tile_prev = v->hash_tile_next;
00540   }
00541 
00542   /* Insert vehicle at beginning of the new position in the hash table */
00543   if (new_hash != NULL) {
00544     v->hash_tile_next = *new_hash;
00545     if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
00546     v->hash_tile_prev = new_hash;
00547     *new_hash = v;
00548   }
00549 
00550   /* Remember current hash position */
00551   v->hash_tile_current = new_hash;
00552 }
00553 
00554 static Vehicle *_vehicle_viewport_hash[0x1000];
00555 
00556 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
00557 {
00558   Vehicle **old_hash, **new_hash;
00559   int old_x = v->coord.left;
00560   int old_y = v->coord.top;
00561 
00562   new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
00563   old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
00564 
00565   if (old_hash == new_hash) return;
00566 
00567   /* remove from hash table? */
00568   if (old_hash != NULL) {
00569     if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
00570     *v->hash_viewport_prev = v->hash_viewport_next;
00571   }
00572 
00573   /* insert into hash table? */
00574   if (new_hash != NULL) {
00575     v->hash_viewport_next = *new_hash;
00576     if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
00577     v->hash_viewport_prev = new_hash;
00578     *new_hash = v;
00579   }
00580 }
00581 
00582 void ResetVehicleHash()
00583 {
00584   Vehicle *v;
00585   FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
00586   memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
00587   memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
00588 }
00589 
00590 void ResetVehicleColourMap()
00591 {
00592   Vehicle *v;
00593   FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00594 }
00595 
00600 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00601 static AutoreplaceMap _vehicles_to_autoreplace;
00602 
00603 void InitializeVehicles()
00604 {
00605   _vehicles_to_autoreplace.Reset();
00606   ResetVehicleHash();
00607 }
00608 
00609 uint CountVehiclesInChain(const Vehicle *v)
00610 {
00611   uint count = 0;
00612   do count++; while ((v = v->Next()) != NULL);
00613   return count;
00614 }
00615 
00620 bool Vehicle::IsEngineCountable() const
00621 {
00622   switch (this->type) {
00623     case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
00624     case VEH_TRAIN:
00625       return !this->IsArticulatedPart() && // tenders and other articulated parts
00626           !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
00627     case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00628     case VEH_SHIP: return true;
00629     default: return false; // Only count company buildable vehicles
00630   }
00631 }
00632 
00637 bool Vehicle::HasEngineType() const
00638 {
00639   switch (this->type) {
00640     case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00641     case VEH_TRAIN:
00642     case VEH_ROAD:
00643     case VEH_SHIP: return true;
00644     default: return false;
00645   }
00646 }
00647 
00653 const Engine *Vehicle::GetEngine() const
00654 {
00655   return Engine::Get(this->engine_type);
00656 }
00657 
00663 const GRFFile *Vehicle::GetGRF() const
00664 {
00665   return this->GetEngine()->GetGRF();
00666 }
00667 
00673 uint32 Vehicle::GetGRFID() const
00674 {
00675   return this->GetEngine()->GetGRFID();
00676 }
00677 
00685 void Vehicle::HandlePathfindingResult(bool path_found)
00686 {
00687   if (path_found) {
00688     /* Route found, is the vehicle marked with "lost" flag? */
00689     if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00690 
00691     /* Clear the flag as the PF's problem was solved. */
00692     ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00693     /* Delete the news item. */
00694     DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00695     return;
00696   }
00697 
00698   /* Were we already lost? */
00699   if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00700 
00701   /* It is first time the problem occurred, set the "lost" flag. */
00702   SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00703   /* Notify user about the event. */
00704   AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
00705   if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00706     SetDParam(0, this->index);
00707     AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00708   }
00709 }
00710 
00712 void Vehicle::PreDestructor()
00713 {
00714   if (CleaningPool()) return;
00715 
00716   if (Station::IsValidID(this->last_station_visited)) {
00717     Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00718 
00719     HideFillingPercent(&this->fill_percent_te_id);
00720 
00721     delete this->cargo_payment;
00722   }
00723 
00724   if (this->IsEngineCountable()) {
00725     GroupStatistics::CountEngine(this, -1);
00726     if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
00727     GroupStatistics::UpdateAutoreplace(this->owner);
00728 
00729     if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00730     DeleteGroupHighlightOfVehicle(this);
00731   }
00732 
00733   if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00734     Aircraft *a = Aircraft::From(this);
00735     Station *st = GetTargetAirportIfValid(a);
00736     if (st != NULL) {
00737       const AirportFTA *layout = st->airport.GetFTA()->layout;
00738       CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00739     }
00740   }
00741 
00742 
00743   if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00744     RoadVehicle *v = RoadVehicle::From(this);
00745     if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00746       /* Leave the drive through roadstop, when you have not already left it. */
00747       RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00748     }
00749   }
00750 
00751   if (this->Previous() == NULL) {
00752     InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00753   }
00754 
00755   if (this->IsPrimaryVehicle()) {
00756     DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00757     DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00758     DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00759     DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00760     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00761     SetWindowDirty(WC_COMPANY, this->owner);
00762     OrderBackup::ClearVehicle(this);
00763   }
00764   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00765 
00766   this->cargo.Truncate(0);
00767   DeleteVehicleOrders(this);
00768   DeleteDepotHighlightOfVehicle(this);
00769 
00770   extern void StopGlobalFollowVehicle(const Vehicle *v);
00771   StopGlobalFollowVehicle(this);
00772 
00773   ReleaseDisastersTargetingVehicle(this->index);
00774 }
00775 
00776 Vehicle::~Vehicle()
00777 {
00778   free(this->name);
00779 
00780   if (CleaningPool()) {
00781     this->cargo.OnCleanPool();
00782     return;
00783   }
00784 
00785   /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
00786    * it may happen that vehicle chain is deleted when visible */
00787   if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00788 
00789   Vehicle *v = this->Next();
00790   this->SetNext(NULL);
00791 
00792   delete v;
00793 
00794   UpdateVehicleTileHash(this, true);
00795   UpdateVehicleViewportHash(this, INVALID_COORD, 0);
00796   DeleteVehicleNews(this->index, INVALID_STRING_ID);
00797   DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00798 }
00799 
00804 void VehicleEnteredDepotThisTick(Vehicle *v)
00805 {
00806   /* Vehicle should stop in the depot if it was in 'stopping' state */
00807   _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00808 
00809   /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
00810    * stopping in the depot, so we stop it to ensure that it will not reserve
00811    * the path out of the depot before we might autoreplace it to a different
00812    * engine. The new engine would not own the reserved path we store that we
00813    * stopped the vehicle, so autoreplace can start it again */
00814   v->vehstatus |= VS_STOPPED;
00815 }
00816 
00822 static void RunVehicleDayProc()
00823 {
00824   if (_game_mode != GM_NORMAL) return;
00825 
00826   /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
00827   for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00828     Vehicle *v = Vehicle::Get(i);
00829     if (v == NULL) continue;
00830 
00831     /* Call the 32-day callback if needed */
00832     if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
00833       uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00834       if (callback != CALLBACK_FAILED) {
00835         if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
00836         if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00837 
00838         if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00839       }
00840     }
00841 
00842     /* This is called once per day for each vehicle, but not in the first tick of the day */
00843     v->OnNewDay();
00844   }
00845 }
00846 
00847 void CallVehicleTicks()
00848 {
00849   _vehicles_to_autoreplace.Clear();
00850 
00851   RunVehicleDayProc();
00852 
00853   Station *st;
00854   FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00855 
00856   Vehicle *v;
00857   FOR_ALL_VEHICLES(v) {
00858     /* Vehicle could be deleted in this tick */
00859     if (!v->Tick()) {
00860       assert(Vehicle::Get(vehicle_index) == NULL);
00861       continue;
00862     }
00863 
00864     assert(Vehicle::Get(vehicle_index) == v);
00865 
00866     switch (v->type) {
00867       default: break;
00868 
00869       case VEH_TRAIN:
00870       case VEH_ROAD:
00871       case VEH_AIRCRAFT:
00872       case VEH_SHIP:
00873         if (v->vcache.cached_cargo_age_period != 0) {
00874           v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00875           if (--v->cargo_age_counter == 0) {
00876             v->cargo.AgeCargo();
00877             v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00878           }
00879         }
00880 
00881         if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00882         if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00883         if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00884 
00885         v->motion_counter += v->cur_speed;
00886         /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
00887         if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00888 
00889         /* Play an alterate running sound every 16 ticks */
00890         if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00891     }
00892   }
00893 
00894   Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00895   for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00896     v = it->first;
00897     /* Autoreplace needs the current company set as the vehicle owner */
00898     cur_company.Change(v->owner);
00899 
00900     /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
00901      * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
00902      * they are already leaving the depot again before being replaced. */
00903     if (it->second) v->vehstatus &= ~VS_STOPPED;
00904 
00905     /* Store the position of the effect as the vehicle pointer will become invalid later */
00906     int x = v->x_pos;
00907     int y = v->y_pos;
00908     int z = v->z_pos;
00909 
00910     const Company *c = Company::Get(_current_company);
00911     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00912     CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00913     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00914 
00915     if (!IsLocalCompany()) continue;
00916 
00917     if (res.Succeeded()) {
00918       ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00919       continue;
00920     }
00921 
00922     StringID error_message = res.GetErrorMessage();
00923     if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00924 
00925     if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00926 
00927     StringID message;
00928     if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00929       message = error_message;
00930     } else {
00931       message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00932     }
00933 
00934     SetDParam(0, v->index);
00935     SetDParam(1, error_message);
00936     AddVehicleNewsItem(message, NS_ADVICE, v->index);
00937   }
00938 
00939   cur_company.Restore();
00940 }
00941 
00946 static void DoDrawVehicle(const Vehicle *v)
00947 {
00948   SpriteID image = v->cur_image;
00949   PaletteID pal = PAL_NONE;
00950 
00951   if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00952 
00953   /* Check whether the vehicle shall be transparent due to the game state */
00954   bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
00955 
00956   if (v->type == VEH_EFFECT) {
00957     /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
00958      * However, transparent smoke and bubbles look weird, so always hide them. */
00959     TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00960     if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00961   }
00962 
00963   AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00964     v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
00965 }
00966 
00971 void ViewportAddVehicles(DrawPixelInfo *dpi)
00972 {
00973   /* The bounding rectangle */
00974   const int l = dpi->left;
00975   const int r = dpi->left + dpi->width;
00976   const int t = dpi->top;
00977   const int b = dpi->top + dpi->height;
00978 
00979   /* The hash area to scan */
00980   int xl, xu, yl, yu;
00981 
00982   if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
00983     xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
00984     xu = GB(r,                        7 + ZOOM_LVL_SHIFT, 6);
00985   } else {
00986     /* scan whole hash row */
00987     xl = 0;
00988     xu = 0x3F;
00989   }
00990 
00991   if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
00992     yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
00993     yu = GB(b,                        6 + ZOOM_LVL_SHIFT, 6) << 6;
00994   } else {
00995     /* scan whole column */
00996     yl = 0;
00997     yu = 0x3F << 6;
00998   }
00999 
01000   for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
01001     for (int x = xl;; x = (x + 1) & 0x3F) {
01002       const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
01003 
01004       while (v != NULL) {
01005         if (!(v->vehstatus & VS_HIDDEN) &&
01006             l <= v->coord.right &&
01007             t <= v->coord.bottom &&
01008             r >= v->coord.left &&
01009             b >= v->coord.top) {
01010           DoDrawVehicle(v);
01011         }
01012         v = v->hash_viewport_next;
01013       }
01014 
01015       if (x == xu) break;
01016     }
01017 
01018     if (y == yu) break;
01019   }
01020 }
01021 
01029 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01030 {
01031   Vehicle *found = NULL, *v;
01032   uint dist, best_dist = UINT_MAX;
01033 
01034   if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01035 
01036   x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01037   y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01038 
01039   FOR_ALL_VEHICLES(v) {
01040     if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01041         x >= v->coord.left && x <= v->coord.right &&
01042         y >= v->coord.top && y <= v->coord.bottom) {
01043 
01044       dist = max(
01045         abs(((v->coord.left + v->coord.right) >> 1) - x),
01046         abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01047       );
01048 
01049       if (dist < best_dist) {
01050         found = v;
01051         best_dist = dist;
01052       }
01053     }
01054   }
01055 
01056   return found;
01057 }
01058 
01063 void DecreaseVehicleValue(Vehicle *v)
01064 {
01065   v->value -= v->value >> 8;
01066   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01067 }
01068 
01069 static const byte _breakdown_chance[64] = {
01070     3,   3,   3,   3,   3,   3,   3,   3,
01071     4,   4,   5,   5,   6,   6,   7,   7,
01072     8,   8,   9,   9,  10,  10,  11,  11,
01073    12,  13,  13,  13,  13,  14,  15,  16,
01074    17,  19,  21,  25,  28,  31,  34,  37,
01075    40,  44,  48,  52,  56,  60,  64,  68,
01076    72,  80,  90, 100, 110, 120, 130, 140,
01077   150, 170, 190, 210, 230, 250, 250, 250,
01078 };
01079 
01080 void CheckVehicleBreakdown(Vehicle *v)
01081 {
01082   int rel, rel_old;
01083 
01084   /* decrease reliability */
01085   v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01086   if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01087 
01088   if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01089       _settings_game.difficulty.vehicle_breakdowns < 1 ||
01090       v->cur_speed < 5 || _game_mode == GM_MENU) {
01091     return;
01092   }
01093 
01094   uint32 r = Random();
01095 
01096   /* increase chance of failure */
01097   int chance = v->breakdown_chance + 1;
01098   if (Chance16I(1, 25, r)) chance += 25;
01099   v->breakdown_chance = min(255, chance);
01100 
01101   /* calculate reliability value to use in comparison */
01102   rel = v->reliability;
01103   if (v->type == VEH_SHIP) rel += 0x6666;
01104 
01105   /* reduced breakdowns? */
01106   if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01107 
01108   /* check if to break down */
01109   if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01110     v->breakdown_ctr    = GB(r, 16, 6) + 0x3F;
01111     v->breakdown_delay  = GB(r, 24, 7) + 0x80;
01112     v->breakdown_chance = 0;
01113   }
01114 }
01115 
01122 bool Vehicle::HandleBreakdown()
01123 {
01124   /* Possible states for Vehicle::breakdown_ctr
01125    * 0  - vehicle is running normally
01126    * 1  - vehicle is currently broken down
01127    * 2  - vehicle is going to break down now
01128    * >2 - vehicle is counting down to the actual breakdown event */
01129   switch (this->breakdown_ctr) {
01130     case 0:
01131       return false;
01132 
01133     case 2:
01134       this->breakdown_ctr = 1;
01135 
01136       if (this->breakdowns_since_last_service != 255) {
01137         this->breakdowns_since_last_service++;
01138       }
01139 
01140       if (this->type == VEH_AIRCRAFT) {
01141         /* Aircraft just need this flag, the rest is handled elsewhere */
01142         this->vehstatus |= VS_AIRCRAFT_BROKEN;
01143       } else {
01144         this->cur_speed = 0;
01145 
01146         if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01147           SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01148             (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01149             (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01150         }
01151 
01152         if (!(this->vehstatus & VS_HIDDEN)) {
01153           EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01154           if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01155         }
01156       }
01157 
01158       this->MarkDirty(); // Update graphics after speed is zeroed
01159       SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01160       SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01161 
01162       /* FALL THROUGH */
01163     case 1:
01164       /* Aircraft breakdowns end only when arriving at the airport */
01165       if (this->type == VEH_AIRCRAFT) return false;
01166 
01167       /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
01168       if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01169         if (--this->breakdown_delay == 0) {
01170           this->breakdown_ctr = 0;
01171           this->MarkDirty();
01172           SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01173         }
01174       }
01175       return true;
01176 
01177     default:
01178       if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01179       return false;
01180   }
01181 }
01182 
01187 void AgeVehicle(Vehicle *v)
01188 {
01189   if (v->age < MAX_DAY) {
01190     v->age++;
01191     if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01192   }
01193 
01194   if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01195 
01196   int age = v->age - v->max_age;
01197   if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01198       age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01199     v->reliability_spd_dec <<= 1;
01200   }
01201 
01202   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01203 
01204   /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
01205   if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01206 
01207   /* Don't warn if a renew is active */
01208   if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01209 
01210   StringID str;
01211   if (age == -DAYS_IN_LEAP_YEAR) {
01212     str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01213   } else if (age == 0) {
01214     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01215   } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01216     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01217   } else {
01218     return;
01219   }
01220 
01221   SetDParam(0, v->index);
01222   AddVehicleNewsItem(str, NS_ADVICE, v->index);
01223 }
01224 
01231 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01232 {
01233   int count = 0;
01234   int max = 0;
01235   int cars = 0;
01236   int unloading = 0;
01237   bool loading = false;
01238 
01239   const Vehicle *u = v;
01240   /* The station may be NULL when the (colour) string does not need to be set. */
01241   const Station *st = Station::GetIfValid(v->last_station_visited);
01242   assert(colour == NULL || st != NULL);
01243 
01244   /* Count up max and used */
01245   for (; v != NULL; v = v->Next()) {
01246     count += v->cargo.Count();
01247     max += v->cargo_cap;
01248     if (v->cargo_cap != 0 && colour != NULL) {
01249       unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01250       loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01251       cars++;
01252     }
01253   }
01254 
01255   if (colour != NULL) {
01256     if (unloading == 0 && loading) {
01257       *colour = STR_PERCENT_UP;
01258     } else if (cars == unloading || !loading) {
01259       *colour = STR_PERCENT_DOWN;
01260     } else {
01261       *colour = STR_PERCENT_UP_DOWN;
01262     }
01263   }
01264 
01265   /* Train without capacity */
01266   if (max == 0) return 100;
01267 
01268   /* Return the percentage */
01269   return (count * 100) / max;
01270 }
01271 
01276 void VehicleEnterDepot(Vehicle *v)
01277 {
01278   /* Always work with the front of the vehicle */
01279   assert(v == v->First());
01280 
01281   switch (v->type) {
01282     case VEH_TRAIN: {
01283       Train *t = Train::From(v);
01284       SetWindowClassesDirty(WC_TRAINS_LIST);
01285       /* Clear path reservation */
01286       SetDepotReservation(t->tile, false);
01287       if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01288 
01289       UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01290       t->wait_counter = 0;
01291       t->force_proceed = TFP_NONE;
01292       ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01293       t->ConsistChanged(true);
01294       break;
01295     }
01296 
01297     case VEH_ROAD:
01298       SetWindowClassesDirty(WC_ROADVEH_LIST);
01299       break;
01300 
01301     case VEH_SHIP: {
01302       SetWindowClassesDirty(WC_SHIPS_LIST);
01303       Ship *ship = Ship::From(v);
01304       ship->state = TRACK_BIT_DEPOT;
01305       ship->UpdateCache();
01306       ship->UpdateViewport(true, true);
01307       SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01308       break;
01309     }
01310 
01311     case VEH_AIRCRAFT:
01312       SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01313       HandleAircraftEnterHangar(Aircraft::From(v));
01314       break;
01315     default: NOT_REACHED();
01316   }
01317   SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01318 
01319   if (v->type != VEH_TRAIN) {
01320     /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
01321      * 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 */
01322     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01323   }
01324   SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01325 
01326   v->vehstatus |= VS_HIDDEN;
01327   v->cur_speed = 0;
01328 
01329   VehicleServiceInDepot(v);
01330 
01331   TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01332 
01333   if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01334     SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01335 
01336     const Order *real_order = v->GetOrder(v->cur_real_order_index);
01337     Order t = v->current_order;
01338     v->current_order.MakeDummy();
01339 
01340     /* Test whether we are heading for this depot. If not, do nothing.
01341      * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
01342     if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01343         real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01344         (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01345       /* We are heading for another depot, keep driving. */
01346       return;
01347     }
01348 
01349     if (t.IsRefit()) {
01350       Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01351       CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01352       cur_company.Restore();
01353 
01354       if (cost.Failed()) {
01355         _vehicles_to_autoreplace[v] = false;
01356         if (v->owner == _local_company) {
01357           /* Notify the user that we stopped the vehicle */
01358           SetDParam(0, v->index);
01359           AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01360         }
01361       } else if (cost.GetCost() != 0) {
01362         v->profit_this_year -= cost.GetCost() << 8;
01363         if (v->owner == _local_company) {
01364           ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01365         }
01366       }
01367     }
01368 
01369     if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01370       /* Part of orders */
01371       v->DeleteUnreachedImplicitOrders();
01372       UpdateVehicleTimetable(v, true);
01373       v->IncrementImplicitOrderIndex();
01374     }
01375     if (t.GetDepotActionType() & ODATFB_HALT) {
01376       /* Vehicles are always stopped on entering depots. Do not restart this one. */
01377       _vehicles_to_autoreplace[v] = false;
01378       if (v->owner == _local_company) {
01379         SetDParam(0, v->index);
01380         AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01381       }
01382       AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01383     }
01384   }
01385 }
01386 
01387 
01393 void VehicleUpdatePosition(Vehicle *v)
01394 {
01395   UpdateVehicleTileHash(v, false);
01396 }
01397 
01404 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01405 {
01406   int img = v->cur_image;
01407   Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01408   const Sprite *spr = GetSprite(img, ST_NORMAL);
01409 
01410   pt.x += spr->x_offs;
01411   pt.y += spr->y_offs;
01412 
01413   UpdateVehicleViewportHash(v, pt.x, pt.y);
01414 
01415   Rect old_coord = v->coord;
01416   v->coord.left   = pt.x;
01417   v->coord.top    = pt.y;
01418   v->coord.right  = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01419   v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01420 
01421   if (dirty) {
01422     if (old_coord.left == INVALID_COORD) {
01423       MarkSingleVehicleDirty(v);
01424     } else {
01425       MarkAllViewportsDirty(
01426         min(old_coord.left,   v->coord.left),
01427         min(old_coord.top,    v->coord.top),
01428         max(old_coord.right,  v->coord.right) + 1 * ZOOM_LVL_BASE,
01429         max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01430       );
01431     }
01432   }
01433 }
01434 
01439 void VehicleUpdatePositionAndViewport(Vehicle *v)
01440 {
01441   VehicleUpdatePosition(v);
01442   VehicleUpdateViewport(v, true);
01443 }
01444 
01449 void MarkSingleVehicleDirty(const Vehicle *v)
01450 {
01451   MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01452 }
01453 
01459 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01460 {
01461   static const int8 _delta_coord[16] = {
01462     -1,-1,-1, 0, 1, 1, 1, 0, /* x */
01463     -1, 0, 1, 1, 1, 0,-1,-1, /* y */
01464   };
01465 
01466   int x = v->x_pos + _delta_coord[v->direction];
01467   int y = v->y_pos + _delta_coord[v->direction + 8];
01468 
01469   GetNewVehiclePosResult gp;
01470   gp.x = x;
01471   gp.y = y;
01472   gp.old_tile = v->tile;
01473   gp.new_tile = TileVirtXY(x, y);
01474   return gp;
01475 }
01476 
01477 static const Direction _new_direction_table[] = {
01478   DIR_N,  DIR_NW, DIR_W,
01479   DIR_NE, DIR_SE, DIR_SW,
01480   DIR_E,  DIR_SE, DIR_S
01481 };
01482 
01483 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01484 {
01485   int i = 0;
01486 
01487   if (y >= v->y_pos) {
01488     if (y != v->y_pos) i += 3;
01489     i += 3;
01490   }
01491 
01492   if (x >= v->x_pos) {
01493     if (x != v->x_pos) i++;
01494     i++;
01495   }
01496 
01497   Direction dir = v->direction;
01498 
01499   DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01500   if (dirdiff == DIRDIFF_SAME) return dir;
01501   return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01502 }
01503 
01513 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01514 {
01515   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01516 }
01517 
01525 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01526 {
01527   /* Find maximum */
01528   const Vehicle *v;
01529   FOR_ALL_VEHICLES(v) {
01530     if (v->type == type && v->owner == owner) {
01531       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01532     }
01533   }
01534 
01535   if (this->maxid == 0) return;
01536 
01537   /* Reserving 'maxid + 2' because we need:
01538    * - space for the last item (with v->unitnumber == maxid)
01539    * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
01540   this->cache = CallocT<bool>(this->maxid + 2);
01541 
01542   /* Fill the cache */
01543   FOR_ALL_VEHICLES(v) {
01544     if (v->type == type && v->owner == owner) {
01545       this->cache[v->unitnumber] = true;
01546     }
01547   }
01548 }
01549 
01551 UnitID FreeUnitIDGenerator::NextID()
01552 {
01553   if (this->maxid <= this->curid) return ++this->curid;
01554 
01555   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
01556 
01557   return this->curid;
01558 }
01559 
01565 UnitID GetFreeUnitNumber(VehicleType type)
01566 {
01567   /* Check whether it is allowed to build another vehicle. */
01568   uint max_veh;
01569   switch (type) {
01570     case VEH_TRAIN:    max_veh = _settings_game.vehicle.max_trains;   break;
01571     case VEH_ROAD:     max_veh = _settings_game.vehicle.max_roadveh;  break;
01572     case VEH_SHIP:     max_veh = _settings_game.vehicle.max_ships;    break;
01573     case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01574     default: NOT_REACHED();
01575   }
01576 
01577   const Company *c = Company::Get(_current_company);
01578   if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
01579 
01580   FreeUnitIDGenerator gen(type, _current_company);
01581 
01582   return gen.NextID();
01583 }
01584 
01585 
01594 bool CanBuildVehicleInfrastructure(VehicleType type)
01595 {
01596   assert(IsCompanyBuildableVehicleType(type));
01597 
01598   if (!Company::IsValidID(_local_company)) return false;
01599   if (!_settings_client.gui.disable_unsuitable_building) return true;
01600 
01601   UnitID max;
01602   switch (type) {
01603     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
01604     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
01605     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
01606     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01607     default: NOT_REACHED();
01608   }
01609 
01610   /* We can build vehicle infrastructure when we may build the vehicle type */
01611   if (max > 0) {
01612     /* Can we actually build the vehicle type? */
01613     const Engine *e;
01614     FOR_ALL_ENGINES_OF_TYPE(e, type) {
01615       if (HasBit(e->company_avail, _local_company)) return true;
01616     }
01617     return false;
01618   }
01619 
01620   /* We should be able to build infrastructure when we have the actual vehicle type */
01621   const Vehicle *v;
01622   FOR_ALL_VEHICLES(v) {
01623     if (v->owner == _local_company && v->type == type) return true;
01624   }
01625 
01626   return false;
01627 }
01628 
01629 
01637 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01638 {
01639   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01640   const Engine *e = Engine::Get(engine_type);
01641   switch (e->type) {
01642     default: NOT_REACHED();
01643     case VEH_TRAIN:
01644       if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01645         /* Wagonoverrides use the colour scheme of the front engine.
01646          * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
01647         engine_type = parent_engine_type;
01648         e = Engine::Get(engine_type);
01649         /* Note: Luckily cargo_type is not needed for engines */
01650       }
01651 
01652       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01653       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01654       if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01655         if (!CargoSpec::Get(cargo_type)->is_freight) {
01656           if (parent_engine_type == INVALID_ENGINE) {
01657             return LS_PASSENGER_WAGON_STEAM;
01658           } else {
01659             switch (RailVehInfo(parent_engine_type)->engclass) {
01660               default: NOT_REACHED();
01661               case EC_STEAM:    return LS_PASSENGER_WAGON_STEAM;
01662               case EC_DIESEL:   return LS_PASSENGER_WAGON_DIESEL;
01663               case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01664               case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01665               case EC_MAGLEV:   return LS_PASSENGER_WAGON_MAGLEV;
01666             }
01667           }
01668         } else {
01669           return LS_FREIGHT_WAGON;
01670         }
01671       } else {
01672         bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01673 
01674         switch (e->u.rail.engclass) {
01675           default: NOT_REACHED();
01676           case EC_STEAM:    return LS_STEAM;
01677           case EC_DIESEL:   return is_mu ? LS_DMU : LS_DIESEL;
01678           case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01679           case EC_MONORAIL: return LS_MONORAIL;
01680           case EC_MAGLEV:   return LS_MAGLEV;
01681         }
01682       }
01683 
01684     case VEH_ROAD:
01685       /* Always use the livery of the front */
01686       if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01687         engine_type = parent_engine_type;
01688         e = Engine::Get(engine_type);
01689         cargo_type = v->First()->cargo_type;
01690       }
01691       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01692       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01693 
01694       /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
01695       if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01696         /* Tram */
01697         return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01698       } else {
01699         /* Bus or truck */
01700         return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01701       }
01702 
01703     case VEH_SHIP:
01704       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01705       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01706       return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01707 
01708     case VEH_AIRCRAFT:
01709       switch (e->u.air.subtype) {
01710         case AIR_HELI: return LS_HELICOPTER;
01711         case AIR_CTOL: return LS_SMALL_PLANE;
01712         case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01713         default: NOT_REACHED();
01714       }
01715   }
01716 }
01717 
01727 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01728 {
01729   const Company *c = Company::Get(company);
01730   LiveryScheme scheme = LS_DEFAULT;
01731 
01732   /* The default livery is always available for use, but its in_use flag determines
01733    * whether any _other_ liveries are in use. */
01734   if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01735     /* Determine the livery scheme to use */
01736     scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01737 
01738     /* Switch back to the default scheme if the resolved scheme is not in use */
01739     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01740   }
01741 
01742   return &c->livery[scheme];
01743 }
01744 
01745 
01746 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01747 {
01748   PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01749 
01750   /* Return cached value if any */
01751   if (map != PAL_NONE) return map;
01752 
01753   const Engine *e = Engine::Get(engine_type);
01754 
01755   /* Check if we should use the colour map callback */
01756   if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01757     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01758     /* Failure means "use the default two-colour" */
01759     if (callback != CALLBACK_FAILED) {
01760       assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) conincidences with default value (PAL_NONE)
01761       map = GB(callback, 0, 14);
01762       /* If bit 14 is set, then the company colours are applied to the
01763        * map else it's returned as-is. */
01764       if (!HasBit(callback, 14)) {
01765         /* Update cache */
01766         if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01767         return map;
01768       }
01769     }
01770   }
01771 
01772   bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01773 
01774   if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01775 
01776   /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
01777   if (!Company::IsValidID(company)) return map;
01778 
01779   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01780 
01781   map += livery->colour1;
01782   if (twocc) map += livery->colour2 * 16;
01783 
01784   /* Update cache */
01785   if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01786   return map;
01787 }
01788 
01795 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01796 {
01797   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01798 }
01799 
01805 PaletteID GetVehiclePalette(const Vehicle *v)
01806 {
01807   if (v->IsGroundVehicle()) {
01808     return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01809   }
01810 
01811   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01812 }
01813 
01817 void Vehicle::DeleteUnreachedImplicitOrders()
01818 {
01819   if (this->IsGroundVehicle()) {
01820     uint16 &gv_flags = this->GetGroundVehicleFlags();
01821     if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01822       /* Do not delete orders, only skip them */
01823       ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01824       this->cur_implicit_order_index = this->cur_real_order_index;
01825       InvalidateVehicleOrder(this, 0);
01826       return;
01827     }
01828   }
01829 
01830   const Order *order = this->GetOrder(this->cur_implicit_order_index);
01831   while (order != NULL) {
01832     if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01833 
01834     if (order->IsType(OT_IMPLICIT)) {
01835       /* Delete order effectively deletes order, so get the next before deleting it. */
01836       order = order->next;
01837       DeleteOrder(this, this->cur_implicit_order_index);
01838     } else {
01839       /* Skip non-implicit orders, e.g. service-orders */
01840       order = order->next;
01841       this->cur_implicit_order_index++;
01842     }
01843 
01844     /* Wrap around */
01845     if (order == NULL) {
01846       order = this->GetOrder(0);
01847       this->cur_implicit_order_index = 0;
01848     }
01849   }
01850 }
01851 
01856 void Vehicle::BeginLoading()
01857 {
01858   assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01859 
01860   if (this->current_order.IsType(OT_GOTO_STATION) &&
01861       this->current_order.GetDestination() == this->last_station_visited) {
01862     this->DeleteUnreachedImplicitOrders();
01863 
01864     /* Now both order indices point to the destination station, and we can start loading */
01865     this->current_order.MakeLoading(true);
01866     UpdateVehicleTimetable(this, true);
01867 
01868     /* Furthermore add the Non Stop flag to mark that this station
01869      * is the actual destination of the vehicle, which is (for example)
01870      * necessary to be known for HandleTrainLoading to determine
01871      * whether the train is lost or not; not marking a train lost
01872      * that arrives at random stations is bad. */
01873     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01874 
01875   } else {
01876     /* We weren't scheduled to stop here. Insert an implicit order
01877      * to show that we are stopping here, but only do that if the order
01878      * list isn't empty.
01879      * While only groundvehicles have implicit orders, e.g. aircraft might still enter
01880      * the 'wrong' terminal when skipping orders etc. */
01881     Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01882     if (this->IsGroundVehicle() && in_list != NULL &&
01883         (!in_list->IsType(OT_IMPLICIT) ||
01884         in_list->GetDestination() != this->last_station_visited)) {
01885       bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01886       /* Do not create consecutive duplicates of implicit orders */
01887       Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01888       if (prev_order == NULL ||
01889           (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01890           prev_order->GetDestination() != this->last_station_visited) {
01891 
01892         /* Prefer deleting implicit orders instead of inserting new ones,
01893          * so test whether the right order follows later */
01894         int target_index = this->cur_implicit_order_index;
01895         bool found = false;
01896         while (target_index != this->cur_real_order_index) {
01897           const Order *order = this->GetOrder(target_index);
01898           if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01899             found = true;
01900             break;
01901           }
01902           target_index++;
01903           if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01904           assert(target_index != this->cur_implicit_order_index); // infinite loop?
01905         }
01906 
01907         if (found) {
01908           if (suppress_implicit_orders) {
01909             /* Skip to the found order */
01910             this->cur_implicit_order_index = target_index;
01911             InvalidateVehicleOrder(this, 0);
01912           } else {
01913             /* Delete all implicit orders up to the station we just reached */
01914             const Order *order = this->GetOrder(this->cur_implicit_order_index);
01915             while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01916               if (order->IsType(OT_IMPLICIT)) {
01917                 /* Delete order effectively deletes order, so get the next before deleting it. */
01918                 order = order->next;
01919                 DeleteOrder(this, this->cur_implicit_order_index);
01920               } else {
01921                 /* Skip non-implicit orders, e.g. service-orders */
01922                 order = order->next;
01923                 this->cur_implicit_order_index++;
01924               }
01925 
01926               /* Wrap around */
01927               if (order == NULL) {
01928                 order = this->GetOrder(0);
01929                 this->cur_implicit_order_index = 0;
01930               }
01931               assert(order != NULL);
01932             }
01933           }
01934         } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01935           /* Insert new implicit order */
01936           Order *implicit_order = new Order();
01937           implicit_order->MakeImplicit(this->last_station_visited);
01938           InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01939           if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01940 
01941           /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
01942            * Reenable it for this vehicle */
01943           uint16 &gv_flags = this->GetGroundVehicleFlags();
01944           ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01945         }
01946       }
01947     }
01948     this->current_order.MakeLoading(false);
01949   }
01950 
01951   Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01952 
01953   PrepareUnload(this);
01954 
01955   SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01956   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01957   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01958   SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01959 
01960   Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01961   this->cur_speed = 0;
01962   this->MarkDirty();
01963 }
01964 
01969 void Vehicle::LeaveStation()
01970 {
01971   assert(this->current_order.IsType(OT_LOADING));
01972 
01973   delete this->cargo_payment;
01974 
01975   /* Only update the timetable if the vehicle was supposed to stop here. */
01976   if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01977 
01978   this->current_order.MakeLeaveStation();
01979   Station *st = Station::Get(this->last_station_visited);
01980   st->loading_vehicles.remove(this);
01981 
01982   HideFillingPercent(&this->fill_percent_te_id);
01983 
01984   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01985     /* Trigger station animation (trains only) */
01986     if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01987 
01988     SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
01989   }
01990 }
01991 
01992 
01998 void Vehicle::HandleLoading(bool mode)
01999 {
02000   switch (this->current_order.GetType()) {
02001     case OT_LOADING: {
02002       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02003 
02004       /* Not the first call for this tick, or still loading */
02005       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02006 
02007       this->PlayLeaveStationSound();
02008 
02009       this->LeaveStation();
02010 
02011       /* Only advance to next order if we just loaded at the current one */
02012       const Order *order = this->GetOrder(this->cur_implicit_order_index);
02013       if (order == NULL ||
02014           (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02015           order->GetDestination() != this->last_station_visited) {
02016         return;
02017       }
02018       break;
02019     }
02020 
02021     case OT_DUMMY: break;
02022 
02023     default: return;
02024   }
02025 
02026   this->IncrementImplicitOrderIndex();
02027 }
02028 
02035 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02036 {
02037   CommandCost ret = CheckOwnership(this->owner);
02038   if (ret.Failed()) return ret;
02039 
02040   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02041   if (this->IsStoppedInDepot()) return CMD_ERROR;
02042 
02043   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02044     bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02045     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02046       /* We called with a different DEPOT_SERVICE setting.
02047        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
02048        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
02049       if (flags & DC_EXEC) {
02050         this->current_order.SetDepotOrderType(ODTF_MANUAL);
02051         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02052         SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02053       }
02054       return CommandCost();
02055     }
02056 
02057     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
02058     if (flags & DC_EXEC) {
02059       /* If the orders to 'goto depot' are in the orders list (forced servicing),
02060        * then skip to the next order; effectively cancelling this forced service */
02061       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02062 
02063       if (this->IsGroundVehicle()) {
02064         uint16 &gv_flags = this->GetGroundVehicleFlags();
02065         SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02066       }
02067 
02068       this->current_order.MakeDummy();
02069       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02070     }
02071     return CommandCost();
02072   }
02073 
02074   TileIndex location;
02075   DestinationID destination;
02076   bool reverse;
02077   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};
02078   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02079 
02080   if (flags & DC_EXEC) {
02081     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02082 
02083     if (this->IsGroundVehicle()) {
02084       uint16 &gv_flags = this->GetGroundVehicleFlags();
02085       SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02086     }
02087 
02088     this->dest_tile = location;
02089     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02090     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02091     SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02092 
02093     /* If there is no depot in front, reverse automatically (trains only) */
02094     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02095 
02096     if (this->type == VEH_AIRCRAFT) {
02097       Aircraft *a = Aircraft::From(this);
02098       if (a->state == FLYING && a->targetairport != destination) {
02099         /* The aircraft is now heading for a different hangar than the next in the orders */
02100         extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02101         AircraftNextAirportPos_and_Order(a);
02102       }
02103     }
02104   }
02105 
02106   return CommandCost();
02107 
02108 }
02109 
02114 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02115 {
02116   bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02117   const Engine *e = this->GetEngine();
02118 
02119   /* Evaluate properties */
02120   byte visual_effect;
02121   switch (e->type) {
02122     case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02123     case VEH_ROAD:  visual_effect = e->u.road.visual_effect; break;
02124     case VEH_SHIP:  visual_effect = e->u.ship.visual_effect; break;
02125     default:        visual_effect = 1 << VE_DISABLE_EFFECT;  break;
02126   }
02127 
02128   /* Check powered wagon / visual effect callback */
02129   if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02130     uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02131 
02132     if (callback != CALLBACK_FAILED) {
02133       if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02134 
02135       callback = GB(callback, 0, 8);
02136       /* Avoid accidentally setting 'visual_effect' to the default value
02137        * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
02138       if (callback == VE_DEFAULT) {
02139         assert(HasBit(callback, VE_DISABLE_EFFECT));
02140         SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02141       }
02142       visual_effect = callback;
02143     }
02144   }
02145 
02146   /* Apply default values */
02147   if (visual_effect == VE_DEFAULT ||
02148       (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02149     /* Only train engines have default effects.
02150      * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
02151     if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02152       if (visual_effect == VE_DEFAULT) {
02153         visual_effect = 1 << VE_DISABLE_EFFECT;
02154       } else {
02155         SetBit(visual_effect, VE_DISABLE_EFFECT);
02156       }
02157     } else {
02158       if (visual_effect == VE_DEFAULT) {
02159         /* Also set the offset */
02160         visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02161       }
02162       SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02163     }
02164   }
02165 
02166   this->vcache.cached_vis_effect = visual_effect;
02167 
02168   if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02169     ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02170     ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02171   }
02172 }
02173 
02174 static const int8 _vehicle_smoke_pos[8] = {
02175   1, 1, 1, 0, -1, -1, -1, 0
02176 };
02177 
02182 void Vehicle::ShowVisualEffect() const
02183 {
02184   assert(this->IsPrimaryVehicle());
02185   bool sound = false;
02186 
02187   /* Do not show any smoke when:
02188    * - vehicle smoke is disabled by the player
02189    * - the vehicle is slowing down or stopped (by the player)
02190    * - the vehicle is moving very slowly
02191    */
02192   if (_settings_game.vehicle.smoke_amount == 0 ||
02193       this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02194       this->cur_speed < 2) {
02195     return;
02196   }
02197 
02198   uint max_speed = this->vcache.cached_max_speed;
02199   if (this->type == VEH_TRAIN) {
02200     const Train *t = Train::From(this);
02201     /* For trains, do not show any smoke when:
02202      * - the train is reversing
02203      * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
02204      */
02205     if (HasBit(t->flags, VRF_REVERSING) ||
02206         (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02207         t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02208       return;
02209     }
02210 
02211     max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02212     max_speed = min(max_speed, this->current_order.max_speed);
02213   }
02214   if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02215 
02216   const Vehicle *v = this;
02217 
02218   do {
02219     int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02220     byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02221     bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02222 
02223     /* Show no smoke when:
02224      * - Smoke has been disabled for this vehicle
02225      * - The vehicle is not visible
02226      * - The vehicle is under a bridge
02227      * - The vehicle is on a depot tile
02228      * - The vehicle is on a tunnel tile
02229      * - The vehicle is a train engine that is currently unpowered */
02230     if (disable_effect ||
02231         v->vehstatus & VS_HIDDEN ||
02232         (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02233         IsDepotTile(v->tile) ||
02234         IsTunnelTile(v->tile) ||
02235         (v->type == VEH_TRAIN &&
02236         !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02237       continue;
02238     }
02239 
02240     /* The effect offset is relative to a point 4 units behind the vehicle's
02241      * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
02242      * correction factor. */
02243     if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02244 
02245     int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02246     int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02247 
02248     if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02249       x = -x;
02250       y = -y;
02251     }
02252 
02253     switch (effect_type) {
02254       case VE_TYPE_STEAM:
02255         /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
02256          * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
02257          * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
02258          * REGULATION:
02259          * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
02260         if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02261           CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02262           sound = true;
02263         }
02264         break;
02265 
02266       case VE_TYPE_DIESEL: {
02267         /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
02268          * when smoke emission stops.
02269          * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
02270          * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
02271          * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
02272          * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
02273          * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
02274          * maximum speed no diesel_smoke is emitted.
02275          * REGULATION:
02276          * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
02277          * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
02278         int power_weight_effect = 0;
02279         if (v->type == VEH_TRAIN) {
02280           power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02281         }
02282         if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02283             Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02284           CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02285           sound = true;
02286         }
02287         break;
02288       }
02289 
02290       case VE_TYPE_ELECTRIC:
02291         /* Electric train's spark - more often occurs when train is departing (more load)
02292          * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
02293          * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
02294          * reaching its max. speed, quarter by quarter of it, chance decreases untill the usuall 2,22% at train's top speed.
02295          * REGULATION:
02296          * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
02297         if (GB(v->tick_counter, 0, 2) == 0 &&
02298             Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02299           CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02300           sound = true;
02301         }
02302         break;
02303 
02304       default:
02305         break;
02306     }
02307   } while ((v = v->Next()) != NULL);
02308 
02309   if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02310 }
02311 
02316 void Vehicle::SetNext(Vehicle *next)
02317 {
02318   assert(this != next);
02319 
02320   if (this->next != NULL) {
02321     /* We had an old next vehicle. Update the first and previous pointers */
02322     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02323       v->first = this->next;
02324     }
02325     this->next->previous = NULL;
02326   }
02327 
02328   this->next = next;
02329 
02330   if (this->next != NULL) {
02331     /* A new next vehicle. Update the first and previous pointers */
02332     if (this->next->previous != NULL) this->next->previous->next = NULL;
02333     this->next->previous = this;
02334     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02335       v->first = this->first;
02336     }
02337   }
02338 }
02339 
02345 void Vehicle::AddToShared(Vehicle *shared_chain)
02346 {
02347   assert(this->previous_shared == NULL && this->next_shared == NULL);
02348 
02349   if (shared_chain->orders.list == NULL) {
02350     assert(shared_chain->previous_shared == NULL);
02351     assert(shared_chain->next_shared == NULL);
02352     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02353   }
02354 
02355   this->next_shared     = shared_chain->next_shared;
02356   this->previous_shared = shared_chain;
02357 
02358   shared_chain->next_shared = this;
02359 
02360   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02361 
02362   shared_chain->orders.list->AddVehicle(this);
02363 }
02364 
02368 void Vehicle::RemoveFromShared()
02369 {
02370   /* Remember if we were first and the old window number before RemoveVehicle()
02371    * as this changes first if needed. */
02372   bool were_first = (this->FirstShared() == this);
02373   VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02374 
02375   this->orders.list->RemoveVehicle(this);
02376 
02377   if (!were_first) {
02378     /* We are not the first shared one, so only relink our previous one. */
02379     this->previous_shared->next_shared = this->NextShared();
02380   }
02381 
02382   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02383 
02384 
02385   if (this->orders.list->GetNumVehicles() == 1) {
02386     /* When there is only one vehicle, remove the shared order list window. */
02387     DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02388     InvalidateVehicleOrder(this->FirstShared(), 0);
02389   } else if (were_first) {
02390     /* If we were the first one, update to the new first one.
02391      * Note: FirstShared() is already the new first */
02392     InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02393   }
02394 
02395   this->next_shared     = NULL;
02396   this->previous_shared = NULL;
02397 }
02398 
02399 void VehiclesYearlyLoop()
02400 {
02401   Vehicle *v;
02402   FOR_ALL_VEHICLES(v) {
02403     if (v->IsPrimaryVehicle()) {
02404       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
02405       Money profit = v->GetDisplayProfitThisYear();
02406       if (v->age >= 730 && profit < 0) {
02407         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02408           SetDParam(0, v->index);
02409           SetDParam(1, profit);
02410           AddVehicleNewsItem(
02411             STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02412             NS_ADVICE,
02413             v->index
02414           );
02415         }
02416         AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02417       }
02418 
02419       v->profit_last_year = v->profit_this_year;
02420       v->profit_this_year = 0;
02421       SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02422     }
02423   }
02424   GroupStatistics::UpdateProfits();
02425   SetWindowClassesDirty(WC_TRAINS_LIST);
02426   SetWindowClassesDirty(WC_SHIPS_LIST);
02427   SetWindowClassesDirty(WC_ROADVEH_LIST);
02428   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02429 }
02430 
02431 
02441 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02442 {
02443   const Engine *e = Engine::GetIfValid(engine_type);
02444   assert(e != NULL);
02445 
02446   switch (e->type) {
02447     case VEH_TRAIN:
02448       return (st->facilities & FACIL_TRAIN) != 0;
02449 
02450     case VEH_ROAD:
02451       /* For road vehicles we need the vehicle to know whether it can actually
02452        * use the station, but if it doesn't have facilities for RVs it is
02453        * certainly not possible that the station can be used. */
02454       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02455 
02456     case VEH_SHIP:
02457       return (st->facilities & FACIL_DOCK) != 0;
02458 
02459     case VEH_AIRCRAFT:
02460       return (st->facilities & FACIL_AIRPORT) != 0 &&
02461           (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02462 
02463     default:
02464       return false;
02465   }
02466 }
02467 
02474 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02475 {
02476   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02477 
02478   return CanVehicleUseStation(v->engine_type, st);
02479 }
02480 
02486 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02487 {
02488   assert(this->IsGroundVehicle());
02489   if (this->type == VEH_TRAIN) {
02490     return &Train::From(this)->gcache;
02491   } else {
02492     return &RoadVehicle::From(this)->gcache;
02493   }
02494 }
02495 
02501 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02502 {
02503   assert(this->IsGroundVehicle());
02504   if (this->type == VEH_TRAIN) {
02505     return &Train::From(this)->gcache;
02506   } else {
02507     return &RoadVehicle::From(this)->gcache;
02508   }
02509 }
02510 
02516 uint16 &Vehicle::GetGroundVehicleFlags()
02517 {
02518   assert(this->IsGroundVehicle());
02519   if (this->type == VEH_TRAIN) {
02520     return Train::From(this)->gv_flags;
02521   } else {
02522     return RoadVehicle::From(this)->gv_flags;
02523   }
02524 }
02525 
02531 const uint16 &Vehicle::GetGroundVehicleFlags() const
02532 {
02533   assert(this->IsGroundVehicle());
02534   if (this->type == VEH_TRAIN) {
02535     return Train::From(this)->gv_flags;
02536   } else {
02537     return RoadVehicle::From(this)->gv_flags;
02538   }
02539 }
02540 
02549 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02550 {
02551   if (v->type == VEH_TRAIN) {
02552     Train *u = Train::From(v);
02553     /* Only include whole vehicles, so start with the first articulated part */
02554     u = u->GetFirstEnginePart();
02555 
02556     /* Include num_vehicles vehicles, not counting articulated parts */
02557     for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02558       do {
02559         /* Include current vehicle in the selection. */
02560         set.Include(u->index);
02561 
02562         /* If the vehicle is multiheaded, add the other part too. */
02563         if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02564 
02565         u = u->Next();
02566       } while (u != NULL && u->IsArticulatedPart());
02567     }
02568   }
02569 }