# 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 "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 "vehicle_gui.h"
    00023 #include "train.h"
    00024 #include "aircraft.h"
    00025 #include "newgrf_debug.h"
    00026 #include "newgrf_sound.h"
    00027 #include "newgrf_station.h"
    00028 #include "group.h"
    00029 #include "group_gui.h"
    00030 #include "strings_func.h"
    00031 #include "zoom_func.h"
    00032 #include "date_func.h"
    00033 #include "window_func.h"
    00034 #include "vehicle_func.h"
    00035 #include "autoreplace_func.h"
    00036 #include "autoreplace_gui.h"
    00037 #include "station_base.h"
    00038 #include "ai/ai.hpp"
    00039 #include "depot_func.h"
    00040 #include "network/network.h"
    00041 #include "core/pool_func.hpp"
    00042 #include "economy_base.h"
    00043 #include "articulated_vehicles.h"
    00044 #include "roadstop_base.h"
    00045 #include "core/random_func.hpp"
    00046 #include "core/backup_type.hpp"
    00047 #include "order_backup.h"
    00048 #include "sound_func.h"
    00049 #include "effectvehicle_func.h"
    00050 #include "effectvehicle_base.h"
    00051 #include "vehiclelist.h"
    00052 #include "bridge_map.h"
    00053 #include "tunnel_map.h"
    00054 #include "depot_map.h"
    00055
    00056 #include "table/strings.h"
    00057
    00058 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
    00059
    00060 VehicleID _new_vehicle_id;
    00061 uint16 _returned_refit_capacity;
    00062 uint16 _returned_mail_refit_capacity;
    00063 byte _age_cargo_skip_counter;
    00064
    00065
    00067 VehiclePool _vehicle_pool("Vehicle");
    00068 INSTANTIATE_POOL_METHODS(Vehicle)
    00069
    00070
    00075 bool Vehicle::NeedsAutorenewing(const Company *c) const
    00076 {
    00077   /* We can always generate the Company pointer when we have the vehicle.
    00078    * However this takes time and since the Company pointer is often present
    00079    * when this function is called then it's faster to pass the pointer as an
    00080    * argument rather than finding it again. */
    00081   assert(c == Company::Get(this->owner));
    00082
    00083   if (!c->settings.engine_renew) return false;
    00084   if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
    00085   if (this->age == 0) return false; // rail cars don't age and lacks a max age
    00086
    00087   return true;
    00088 }
    00089
    00090 void VehicleServiceInDepot(Vehicle *v)
    00091 {
    00092   v->date_of_last_service = _date;
    00093   v->breakdowns_since_last_service = 0;
    00094   v->reliability = Engine::Get(v->engine_type)->reliability;
    00095   SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
    00096 }
    00097
    00104 bool Vehicle::NeedsServicing() const
    00105 {
    00106   /* Stopped or crashed vehicles will not move, as such making unmovable
    00107    * vehicles to go for service is lame. */
    00108   if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
    00109
    00110   /* Are we ready for the next service cycle? */
    00111   const Company *c = Company::Get(this->owner);
    00112   if (c->settings.vehicle.servint_ispercent ?
    00113       (this->reliability >= Engine::Get(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
    00114       (this->date_of_last_service + this->service_interval >= _date)) {
    00115     return false;
    00116   }
    00117
    00118   /* If we're servicing anyway, because we have not disabled servicing when
    00119    * there are no breakdowns or we are playing with breakdowns, bail out. */
    00120   if (!_settings_game.order.no_servicing_if_no_breakdowns ||
    00121       _settings_game.difficulty.vehicle_breakdowns != 0) {
    00122     return true;
    00123   }
    00124
    00125   /* Test whether there is some pending autoreplace.
    00126    * Note: We do this after the service-interval test.
    00127    * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
    00128   bool pending_replace = false;
    00129   Money needed_money = c->settings.engine_renew_money;
    00130   if (needed_money > c->money) return false;
    00131
    00132   for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
    00133     EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
    00134
    00135     /* Check engine availability */
    00136     if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
    00137
    00138     /* Check refittability */
    00139     uint32 available_cargo_types, union_mask;
    00140     GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
    00141     /* Is there anything to refit? */
    00142     if (union_mask != 0) {
    00143       CargoID cargo_type;
    00144       /* We cannot refit to mixed cargoes in an automated way */
    00145       if (IsArticulatedVehicleCarryingDifferentCargos(v, &cargo_type)) continue;
    00146
    00147       /* Did the old vehicle carry anything? */
    00148       if (cargo_type != CT_INVALID) {
    00149         /* We can't refit the vehicle to carry the cargo we want */
    00150         if (!HasBit(available_cargo_types, cargo_type)) continue;
    00151       }
    00152     }
    00153
    00154     /* Check money.
    00155      * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
    00156     pending_replace = true;
    00157     needed_money += 2 * Engine::Get(new_engine)->GetCost();
    00158     if (needed_money > c->money) return false;
    00159   }
    00160
    00161   return pending_replace;
    00162 }
    00163
    00169 bool Vehicle::NeedsAutomaticServicing() const
    00170 {
    00171   if (this->HasDepotOrder()) return false;
    00172   if (this->current_order.IsType(OT_LOADING)) return false;
    00173   if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
    00174   return NeedsServicing();
    00175 }
    00176
    00177 uint Vehicle::Crash(bool flooded)
    00178 {
    00179   assert((this->vehstatus & VS_CRASHED) == 0);
    00180   assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
    00181
    00182   uint pass = 0;
    00183   /* Stop the vehicle. */
    00184   if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
    00185   /* crash all wagons, and count passengers */
    00186   for (Vehicle *v = this; v != NULL; v = v->Next()) {
    00187     if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
    00188     v->vehstatus |= VS_CRASHED;
    00189     MarkSingleVehicleDirty(v);
    00190   }
    00191
    00192   /* Dirty some windows */
    00193   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
    00194   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    00195   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
    00196   SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
    00197
    00198   return pass;
    00199 }
    00200
    00201
    00210 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
    00211 {
    00212   const Engine *e = Engine::Get(engine);
    00213   uint32 grfid = e->grf_prop.grffile->grfid;
    00214   GRFConfig *grfconfig = GetGRFConfig(grfid);
    00215
    00216   if (!HasBit(grfconfig->grf_bugs, bug_type)) {
    00217     SetBit(grfconfig->grf_bugs, bug_type);
    00218     SetDParamStr(0, grfconfig->GetName());
    00219     SetDParam(1, engine);
    00220     ShowErrorMessage(part1, part2, WL_CRITICAL);
    00221     if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
    00222   }
    00223
    00224   /* debug output */
    00225   char buffer[512];
    00226
    00227   SetDParamStr(0, grfconfig->GetName());
    00228   GetString(buffer, part1, lastof(buffer));
    00229   DEBUG(grf, 0, "%s", buffer + 3);
    00230
    00231   SetDParam(1, engine);
    00232   GetString(buffer, part2, lastof(buffer));
    00233   DEBUG(grf, 0, "%s", buffer + 3);
    00234 }
    00235
    00240 Vehicle::Vehicle(VehicleType type)
    00241 {
    00242   this->type               = type;
    00243   this->coord.left         = INVALID_COORD;
    00244   this->group_id           = DEFAULT_GROUP;
    00245   this->fill_percent_te_id = INVALID_TE_ID;
    00246   this->first              = this;
    00247   this->colourmap          = PAL_NONE;
    00248 }
    00249
    00254 byte VehicleRandomBits()
    00255 {
    00256   return GB(Random(), 0, 8);
    00257 }
    00258
    00259 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
    00260  * lookup times at the expense of memory usage. */
    00261 const int HASH_BITS = 7;
    00262 const int HASH_SIZE = 1 << HASH_BITS;
    00263 const int HASH_MASK = HASH_SIZE - 1;
    00264 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
    00265 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
    00266
    00267 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
    00268  * Profiling results show that 0 is fastest. */
    00269 const int HASH_RES = 0;
    00270
    00271 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
    00272
    00273 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
    00274 {
    00275   for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
    00276     for (int x = xl; ; x = (x + 1) & HASH_MASK) {
    00277       Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
    00278       for (; v != NULL; v = v->next_new_hash) {
    00279         Vehicle *a = proc(v, data);
    00280         if (find_first && a != NULL) return a;
    00281       }
    00282       if (x == xu) break;
    00283     }
    00284     if (y == yu) break;
    00285   }
    00286
    00287   return NULL;
    00288 }
    00289
    00290
    00302 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
    00303 {
    00304   const int COLL_DIST = 6;
    00305
    00306   /* Hash area to scan is from xl,yl to xu,yu */
    00307   int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
    00308   int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
    00309   int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
    00310   int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
    00311
    00312   return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
    00313 }
    00314
    00329 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
    00330 {
    00331   VehicleFromPosXY(x, y, data, proc, false);
    00332 }
    00333
    00345 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
    00346 {
    00347   return VehicleFromPosXY(x, y, data, proc, true) != NULL;
    00348 }
    00349
    00360 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
    00361 {
    00362   int x = GB(TileX(tile), HASH_RES, HASH_BITS);
    00363   int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
    00364
    00365   Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
    00366   for (; v != NULL; v = v->next_new_hash) {
    00367     if (v->tile != tile) continue;
    00368
    00369     Vehicle *a = proc(v, data);
    00370     if (find_first && a != NULL) return a;
    00371   }
    00372
    00373   return NULL;
    00374 }
    00375
    00389 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
    00390 {
    00391   VehicleFromPos(tile, data, proc, false);
    00392 }
    00393
    00404 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
    00405 {
    00406   return VehicleFromPos(tile, data, proc, true) != NULL;
    00407 }
    00408
    00415 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
    00416 {
    00417   byte z = *(byte*)data;
    00418
    00419   if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
    00420   if (v->z_pos > z) return NULL;
    00421
    00422   return v;
    00423 }
    00424
    00430 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
    00431 {
    00432   byte z = GetTileMaxZ(tile);
    00433
    00434   /* Value v is not safe in MP games, however, it is used to generate a local
    00435    * error message only (which may be different for different machines).
    00436    * Such a message does not affect MP synchronisation.
    00437    */
    00438   Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
    00439   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
    00440   return CommandCost();
    00441 }
    00442
    00444 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
    00445 {
    00446   if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
    00447   if (v == (const Vehicle *)data) return NULL;
    00448
    00449   return v;
    00450 }
    00451
    00459 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
    00460 {
    00461   /* Value v is not safe in MP games, however, it is used to generate a local
    00462    * error message only (which may be different for different machines).
    00463    * Such a message does not affect MP synchronisation.
    00464    */
    00465   Vehicle *v = VehicleFromPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
    00466   if (v == NULL) v = VehicleFromPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
    00467
    00468   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
    00469   return CommandCost();
    00470 }
    00471
    00472 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
    00473 {
    00474   TrackBits rail_bits = *(TrackBits *)data;
    00475
    00476   if (v->type != VEH_TRAIN) return NULL;
    00477
    00478   Train *t = Train::From(v);
    00479   if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
    00480
    00481   return v;
    00482 }
    00483
    00492 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
    00493 {
    00494   /* Value v is not safe in MP games, however, it is used to generate a local
    00495    * error message only (which may be different for different machines).
    00496    * Such a message does not affect MP synchronisation.
    00497    */
    00498   Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
    00499   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
    00500   return CommandCost();
    00501 }
    00502
    00503 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
    00504 {
    00505   Vehicle **old_hash = v->old_new_hash;
    00506   Vehicle **new_hash;
    00507
    00508   if (remove) {
    00509     new_hash = NULL;
    00510   } else {
    00511     int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
    00512     int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
    00513     new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
    00514   }
    00515
    00516   if (old_hash == new_hash) return;
    00517
    00518   /* Remove from the old position in the hash table */
    00519   if (old_hash != NULL) {
    00520     if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
    00521     *v->prev_new_hash = v->next_new_hash;
    00522   }
    00523
    00524   /* Insert vehicle at beginning of the new position in the hash table */
    00525   if (new_hash != NULL) {
    00526     v->next_new_hash = *new_hash;
    00527     if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
    00528     v->prev_new_hash = new_hash;
    00529     *new_hash = v;
    00530   }
    00531
    00532   /* Remember current hash position */
    00533   v->old_new_hash = new_hash;
    00534 }
    00535
    00536 static Vehicle *_vehicle_position_hash[0x1000];
    00537
    00538 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
    00539 {
    00540   UpdateNewVehiclePosHash(v, x == INVALID_COORD);
    00541
    00542   Vehicle **old_hash, **new_hash;
    00543   int old_x = v->coord.left;
    00544   int old_y = v->coord.top;
    00545
    00546   new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
    00547   old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
    00548
    00549   if (old_hash == new_hash) return;
    00550
    00551   /* remove from hash table? */
    00552   if (old_hash != NULL) {
    00553     if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
    00554     *v->prev_hash = v->next_hash;
    00555   }
    00556
    00557   /* insert into hash table? */
    00558   if (new_hash != NULL) {
    00559     v->next_hash = *new_hash;
    00560     if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
    00561     v->prev_hash = new_hash;
    00562     *new_hash = v;
    00563   }
    00564 }
    00565
    00566 void ResetVehiclePosHash()
    00567 {
    00568   Vehicle *v;
    00569   FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
    00570   memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
    00571   memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
    00572 }
    00573
    00574 void ResetVehicleColourMap()
    00575 {
    00576   Vehicle *v;
    00577   FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
    00578 }
    00579
    00584 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
    00585 static AutoreplaceMap _vehicles_to_autoreplace;
    00586
    00587 void InitializeVehicles()
    00588 {
    00589   _vehicle_pool.CleanPool();
    00590   _cargo_payment_pool.CleanPool();
    00591
    00592   _age_cargo_skip_counter = 1;
    00593
    00594   _vehicles_to_autoreplace.Reset();
    00595   ResetVehiclePosHash();
    00596 }
    00597
    00598 uint CountVehiclesInChain(const Vehicle *v)
    00599 {
    00600   uint count = 0;
    00601   do count++; while ((v = v->Next()) != NULL);
    00602   return count;
    00603 }
    00604
    00610 void CountCompanyVehicles(CompanyID cid, uint counts[4])
    00611 {
    00612   for (uint i = 0; i < 4; i++) counts[i] = 0;
    00613
    00614   const Vehicle *v;
    00615   FOR_ALL_VEHICLES(v) {
    00616     if (v->owner == cid && v->IsPrimaryVehicle()) counts[v->type]++;
    00617   }
    00618 }
    00619
    00624 bool Vehicle::IsEngineCountable() const
    00625 {
    00626   switch (this->type) {
    00627     case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
    00628     case VEH_TRAIN:
    00629       return !this->IsArticulatedPart() && // tenders and other articulated parts
    00630           !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
    00631     case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
    00632     case VEH_SHIP: return true;
    00633     default: return false; // Only count company buildable vehicles
    00634   }
    00635 }
    00636
    00644 void Vehicle::HandlePathfindingResult(bool path_found)
    00645 {
    00646   if (path_found) {
    00647     /* Route found, is the vehicle marked with "lost" flag? */
    00648     if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
    00649
    00650     /* Clear the flag as the PF's problem was solved. */
    00651     ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
    00652     /* Delete the news item. */
    00653     DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
    00654     return;
    00655   }
    00656
    00657   /* Were we already lost? */
    00658   if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
    00659
    00660   /* It is first time the problem occurred, set the "lost" flag. */
    00661   SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
    00662   /* Notify user about the event. */
    00663   AI::NewEvent(this->owner, new AIEventVehicleLost(this->index));
    00664   if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
    00665     SetDParam(0, this->index);
    00666     AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
    00667   }
    00668 }
    00669
    00671 void Vehicle::PreDestructor()
    00672 {
    00673   if (CleaningPool()) return;
    00674
    00675   if (Station::IsValidID(this->last_station_visited)) {
    00676     Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
    00677
    00678     HideFillingPercent(&this->fill_percent_te_id);
    00679
    00680     delete this->cargo_payment;
    00681   }
    00682
    00683   if (this->IsEngineCountable()) {
    00684     Company::Get(this->owner)->num_engines[this->engine_type]--;
    00685     if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
    00686
    00687     DeleteGroupHighlightOfVehicle(this);
    00688     if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
    00689     if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
    00690   }
    00691
    00692   if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
    00693     Aircraft *a = Aircraft::From(this);
    00694     Station *st = GetTargetAirportIfValid(a);
    00695     if (st != NULL) {
    00696       const AirportFTA *layout = st->airport.GetFTA()->layout;
    00697       CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
    00698     }
    00699   }
    00700
    00701
    00702   if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
    00703     RoadVehicle *v = RoadVehicle::From(this);
    00704     if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
    00705       /* Leave the drive through roadstop, when you have not already left it. */
    00706       RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
    00707     }
    00708   }
    00709
    00710   if (this->Previous() == NULL) {
    00711     InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
    00712   }
    00713
    00714   if (this->IsPrimaryVehicle()) {
    00715     DeleteWindowById(WC_VEHICLE_VIEW, this->index);
    00716     DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
    00717     DeleteWindowById(WC_VEHICLE_REFIT, this->index);
    00718     DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
    00719     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
    00720     SetWindowDirty(WC_COMPANY, this->owner);
    00721     OrderBackup::ClearVehicle(this);
    00722   }
    00723   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
    00724
    00725   this->cargo.Truncate(0);
    00726   DeleteVehicleOrders(this);
    00727   DeleteDepotHighlightOfVehicle(this);
    00728
    00729   extern void StopGlobalFollowVehicle(const Vehicle *v);
    00730   StopGlobalFollowVehicle(this);
    00731
    00732   ReleaseDisastersTargetingVehicle(this->index);
    00733 }
    00734
    00735 Vehicle::~Vehicle()
    00736 {
    00737   free(this->name);
    00738
    00739   if (CleaningPool()) return;
    00740
    00741   /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
    00742    * it may happen that vehicle chain is deleted when visible */
    00743   if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
    00744
    00745   Vehicle *v = this->Next();
    00746   this->SetNext(NULL);
    00747
    00748   delete v;
    00749
    00750   UpdateVehiclePosHash(this, INVALID_COORD, 0);
    00751   DeleteVehicleNews(this->index, INVALID_STRING_ID);
    00752   DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
    00753 }
    00754
    00759 void VehicleEnteredDepotThisTick(Vehicle *v)
    00760 {
    00761   /* Vehicle should stop in the depot if it was in 'stopping' state */
    00762   _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
    00763
    00764   /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
    00765    * stopping in the depot, so we stop it to ensure that it will not reserve
    00766    * the path out of the depot before we might autoreplace it to a different
    00767    * engine. The new engine would not own the reserved path we store that we
    00768    * stopped the vehicle, so autoreplace can start it again */
    00769   v->vehstatus |= VS_STOPPED;
    00770 }
    00771
    00777 static void RunVehicleDayProc()
    00778 {
    00779   if (_game_mode != GM_NORMAL) return;
    00780
    00781   /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
    00782   for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
    00783     Vehicle *v = Vehicle::Get(i);
    00784     if (v == NULL) continue;
    00785
    00786     /* Call the 32-day callback if needed */
    00787     if ((v->day_counter & 0x1F) == 0) {
    00788       uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
    00789       if (callback != CALLBACK_FAILED) {
    00790         if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
    00791         if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
    00792       }
    00793     }
    00794
    00795     /* This is called once per day for each vehicle, but not in the first tick of the day */
    00796     v->OnNewDay();
    00797   }
    00798 }
    00799
    00800 void CallVehicleTicks()
    00801 {
    00802   _vehicles_to_autoreplace.Clear();
    00803
    00804   _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
    00805
    00806   RunVehicleDayProc();
    00807
    00808   Station *st;
    00809   FOR_ALL_STATIONS(st) LoadUnloadStation(st);
    00810
    00811   Vehicle *v;
    00812   FOR_ALL_VEHICLES(v) {
    00813     /* Vehicle could be deleted in this tick */
    00814     if (!v->Tick()) {
    00815       assert(Vehicle::Get(vehicle_index) == NULL);
    00816       continue;
    00817     }
    00818
    00819     assert(Vehicle::Get(vehicle_index) == v);
    00820
    00821     switch (v->type) {
    00822       default: break;
    00823
    00824       case VEH_TRAIN:
    00825       case VEH_ROAD:
    00826       case VEH_AIRCRAFT:
    00827       case VEH_SHIP:
    00828         if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
    00829
    00830         if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
    00831         if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
    00832         if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
    00833
    00834         v->motion_counter += v->cur_speed;
    00835         /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
    00836         if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
    00837
    00838         /* Play an alterate running sound every 16 ticks */
    00839         if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
    00840     }
    00841   }
    00842
    00843   Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
    00844   for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
    00845     v = it->first;
    00846     /* Autoreplace needs the current company set as the vehicle owner */
    00847     cur_company.Change(v->owner);
    00848
    00849     /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
    00850      * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
    00851      * they are already leaving the depot again before being replaced. */
    00852     if (it->second) v->vehstatus &= ~VS_STOPPED;
    00853
    00854     /* Store the position of the effect as the vehicle pointer will become invalid later */
    00855     int x = v->x_pos;
    00856     int y = v->y_pos;
    00857     int z = v->z_pos;
    00858
    00859     const Company *c = Company::Get(_current_company);
    00860     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
    00861     CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
    00862     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
    00863
    00864     if (!IsLocalCompany()) continue;
    00865
    00866     if (res.Succeeded()) {
    00867       ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
    00868       continue;
    00869     }
    00870
    00871     StringID error_message = res.GetErrorMessage();
    00872     if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
    00873
    00874     if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
    00875
    00876     StringID message;
    00877     if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
    00878       message = error_message;
    00879     } else {
    00880       message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
    00881     }
    00882
    00883     SetDParam(0, v->index);
    00884     SetDParam(1, error_message);
    00885     AddVehicleNewsItem(message, NS_ADVICE, v->index);
    00886   }
    00887
    00888   cur_company.Restore();
    00889 }
    00890
    00895 static void DoDrawVehicle(const Vehicle *v)
    00896 {
    00897   SpriteID image = v->cur_image;
    00898   PaletteID pal = PAL_NONE;
    00899
    00900   if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
    00901   if ((v->vehstatus & VS_SHADOW) != 0)
    00902   {
    00903     SetBit(image, PALETTE_MODIFIER_SHADOW);
    00904   }
    00905   AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
    00906     v->x_extent, v->y_extent, v->z_extent, v->z_pos, false);
    00907 }
    00908
    00913 void ViewportAddVehicles(DrawPixelInfo *dpi)
    00914 {
    00915   /* The bounding rectangle */
    00916   const int l = dpi->left;
    00917   const int r = dpi->left + dpi->width;
    00918   const int t = dpi->top;
    00919   const int b = dpi->top + dpi->height;
    00920
    00921   /* The hash area to scan */
    00922   int xl, xu, yl, yu;
    00923
    00924   if (dpi->width + 70 < (1 << (7 + 6))) {
    00925     xl = GB(l - 70, 7, 6);
    00926     xu = GB(r,      7, 6);
    00927   } else {
    00928     /* scan whole hash row */
    00929     xl = 0;
    00930     xu = 0x3F;
    00931   }
    00932
    00933   if (dpi->height + 70 < (1 << (6 + 6))) {
    00934     yl = GB(t - 70, 6, 6) << 6;
    00935     yu = GB(b,      6, 6) << 6;
    00936   } else {
    00937     /* scan whole column */
    00938     yl = 0;
    00939     yu = 0x3F << 6;
    00940   }
    00941
    00942   for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
    00943     for (int x = xl;; x = (x + 1) & 0x3F) {
    00944       const Vehicle *v = _vehicle_position_hash[x + y]; // already masked & 0xFFF
    00945
    00946       while (v != NULL) {
    00947         if (!(v->vehstatus & VS_HIDDEN) ) {
    00948           DoDrawVehicle(v);
    00949         }
    00950         v = v->next_hash;
    00951       }
    00952
    00953       if (x == xu) break;
    00954     }
    00955
    00956     if (y == yu) break;
    00957   }
    00958 }
    00959
    00967 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
    00968 {
    00969   Vehicle *found = NULL, *v;
    00970   uint dist, best_dist = UINT_MAX;
    00971
    00972   if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
    00973
    00974   x = ScaleByZoom(x + vp->virtual_left, vp->zoom);
    00975   y = ScaleByZoom(y + vp->virtual_top, vp->zoom) ;
    00976
    00977   FOR_ALL_VEHICLES(v) {
    00978     if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
    00979         x >= v->coord.left && x <= v->coord.right &&
    00980         y >= v->coord.top && y <= v->coord.bottom) {
    00981
    00982       dist = max(
    00983         abs(((v->coord.left + v->coord.right) >> 1) - x),
    00984         abs(((v->coord.top + v->coord.bottom) >> 1) - y)
    00985       );
    00986
    00987       if (dist < best_dist) {
    00988         found = v;
    00989         best_dist = dist;
    00990       }
    00991     }
    00992   }
    00993
    00994   return found;
    00995 }
    00996
    01001 void DecreaseVehicleValue(Vehicle *v)
    01002 {
    01003   v->value -= v->value >> 8;
    01004   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    01005 }
    01006
    01007 static const byte _breakdown_chance[64] = {
    01008     3,   3,   3,   3,   3,   3,   3,   3,
    01009     4,   4,   5,   5,   6,   6,   7,   7,
    01010     8,   8,   9,   9,  10,  10,  11,  11,
    01011    12,  13,  13,  13,  13,  14,  15,  16,
    01012    17,  19,  21,  25,  28,  31,  34,  37,
    01013    40,  44,  48,  52,  56,  60,  64,  68,
    01014    72,  80,  90, 100, 110, 120, 130, 140,
    01015   150, 170, 190, 210, 230, 250, 250, 250,
    01016 };
    01017
    01018 void CheckVehicleBreakdown(Vehicle *v)
    01019 {
    01020   int rel, rel_old;
    01021
    01022   /* decrease reliability */
    01023   v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
    01024   if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    01025
    01026   if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
    01027       _settings_game.difficulty.vehicle_breakdowns < 1 ||
    01028       v->cur_speed < 5 || _game_mode == GM_MENU) {
    01029     return;
    01030   }
    01031
    01032   uint32 r = Random();
    01033
    01034   /* increase chance of failure */
    01035   int chance = v->breakdown_chance + 1;
    01036   if (Chance16I(1, 25, r)) chance += 25;
    01037   v->breakdown_chance = min(255, chance);
    01038
    01039   /* calculate reliability value to use in comparison */
    01040   rel = v->reliability;
    01041   if (v->type == VEH_SHIP) rel += 0x6666;
    01042
    01043   /* reduced breakdowns? */
    01044   if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
    01045
    01046   /* check if to break down */
    01047   if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
    01048     v->breakdown_ctr    = GB(r, 16, 6) + 0x3F;
    01049     v->breakdown_delay  = GB(r, 24, 7) + 0x80;
    01050     v->breakdown_chance = 0;
    01051   }
    01052 }
    01053
    01060 bool Vehicle::HandleBreakdown()
    01061 {
    01062   /* Possible states for Vehicle::breakdown_ctr
    01063    * 0  - vehicle is running normally
    01064    * 1  - vehicle is currently broken down
    01065    * 2  - vehicle is going to break down now
    01066    * >2 - vehicle is counting down to the actual breakdown event */
    01067   switch (this->breakdown_ctr) {
    01068     case 0:
    01069       return false;
    01070
    01071     case 2:
    01072       this->breakdown_ctr = 1;
    01073
    01074       if (this->breakdowns_since_last_service != 255) {
    01075         this->breakdowns_since_last_service++;
    01076       }
    01077
    01078       this->MarkDirty();
    01079       SetWindowDirty(WC_VEHICLE_VIEW, this->index);
    01080       SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
    01081
    01082       if (this->type == VEH_AIRCRAFT) {
    01083         /* Aircraft just need this flag, the rest is handled elsewhere */
    01084         this->vehstatus |= VS_AIRCRAFT_BROKEN;
    01085       } else {
    01086         this->cur_speed = 0;
    01087
    01088         if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
    01089           SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
    01090             (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
    01091             (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
    01092         }
    01093
    01094         if (!(this->vehstatus & VS_HIDDEN)) {
    01095           EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
    01096           if (u != NULL) u->animation_state = this->breakdown_delay * 2;
    01097         }
    01098       }
    01099       /* FALL THROUGH */
    01100     case 1:
    01101       /* Aircraft breakdowns end only when arriving at the airport */
    01102       if (this->type == VEH_AIRCRAFT) return false;
    01103
    01104       /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
    01105       if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
    01106         if (--this->breakdown_delay == 0) {
    01107           this->breakdown_ctr = 0;
    01108           this->MarkDirty();
    01109           SetWindowDirty(WC_VEHICLE_VIEW, this->index);
    01110         }
    01111       }
    01112       return true;
    01113
    01114     default:
    01115       if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
    01116       return false;
    01117   }
    01118 }
    01119
    01124 void AgeVehicle(Vehicle *v)
    01125 {
    01126   if (v->age < MAX_DAY) v->age++;
    01127
    01128   int age = v->age - v->max_age;
    01129   if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
    01130       age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
    01131     v->reliability_spd_dec <<= 1;
    01132   }
    01133
    01134   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    01135
    01136   /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
    01137   if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
    01138
    01139   /* Don't warn if a renew is active */
    01140   if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
    01141
    01142   StringID str;
    01143   if (age == -DAYS_IN_LEAP_YEAR) {
    01144     str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
    01145   } else if (age == 0) {
    01146     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
    01147   } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
    01148     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
    01149   } else {
    01150     return;
    01151   }
    01152
    01153   SetDParam(0, v->index);
    01154   AddVehicleNewsItem(str, NS_ADVICE, v->index);
    01155 }
    01156
    01163 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
    01164 {
    01165   int count = 0;
    01166   int max = 0;
    01167   int cars = 0;
    01168   int unloading = 0;
    01169   bool loading = false;
    01170
    01171   const Vehicle *u = v;
    01172   /* The station may be NULL when the (colour) string does not need to be set. */
    01173   const Station *st = Station::GetIfValid(v->last_station_visited);
    01174   assert(colour == NULL || st != NULL);
    01175
    01176   /* Count up max and used */
    01177   for (; v != NULL; v = v->Next()) {
    01178     count += v->cargo.Count();
    01179     max += v->cargo_cap;
    01180     if (v->cargo_cap != 0 && colour != NULL) {
    01181       unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
    01182       loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
    01183       cars++;
    01184     }
    01185   }
    01186
    01187   if (colour != NULL) {
    01188     if (unloading == 0 && loading) {
    01189       *colour = STR_PERCENT_UP;
    01190     } else if (cars == unloading || !loading) {
    01191       *colour = STR_PERCENT_DOWN;
    01192     } else {
    01193       *colour = STR_PERCENT_UP_DOWN;
    01194     }
    01195   }
    01196
    01197   /* Train without capacity */
    01198   if (max == 0) return 100;
    01199
    01200   /* Return the percentage */
    01201   return (count * 100) / max;
    01202 }
    01203
    01208 void VehicleEnterDepot(Vehicle *v)
    01209 {
    01210   /* Always work with the front of the vehicle */
    01211   assert(v == v->First());
    01212
    01213   switch (v->type) {
    01214     case VEH_TRAIN: {
    01215       Train *t = Train::From(v);
    01216       SetWindowClassesDirty(WC_TRAINS_LIST);
    01217       /* Clear path reservation */
    01218       SetDepotReservation(t->tile, false);
    01219       if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
    01220
    01221       UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
    01222       t->wait_counter = 0;
    01223       t->force_proceed = TFP_NONE;
    01224       ClrBit(t->flags, VRF_TOGGLE_REVERSE);
    01225       t->ConsistChanged(true);
    01226       break;
    01227     }
    01228
    01229     case VEH_ROAD:
    01230       SetWindowClassesDirty(WC_ROADVEH_LIST);
    01231       break;
    01232
    01233     case VEH_SHIP: {
    01234       SetWindowClassesDirty(WC_SHIPS_LIST);
    01235       Ship *ship = Ship::From(v);
    01236       ship->state = TRACK_BIT_DEPOT;
    01237       ship->UpdateCache();
    01238       ship->UpdateViewport(true, true);
    01239       SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
    01240       break;
    01241     }
    01242
    01243     case VEH_AIRCRAFT:
    01244       SetWindowClassesDirty(WC_AIRCRAFT_LIST);
    01245       HandleAircraftEnterHangar(Aircraft::From(v));
    01246       break;
    01247     default: NOT_REACHED();
    01248   }
    01249   SetWindowDirty(WC_VEHICLE_VIEW, v->index);
    01250
    01251   if (v->type != VEH_TRAIN) {
    01252     /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
    01253      * 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 */
    01254     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    01255   }
    01256   SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
    01257
    01258   v->vehstatus |= VS_HIDDEN;
    01259   v->cur_speed = 0;
    01260
    01261   VehicleServiceInDepot(v);
    01262
    01263   TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
    01264
    01265   if (v->current_order.IsType(OT_GOTO_DEPOT)) {
    01266     SetWindowDirty(WC_VEHICLE_VIEW, v->index);
    01267
    01268     const Order *real_order = v->GetOrder(v->cur_real_order_index);
    01269     Order t = v->current_order;
    01270     v->current_order.MakeDummy();
    01271
    01272     /* Test whether we are heading for this depot. If not, do nothing.
    01273      * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
    01274     if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
    01275         real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
    01276         (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
    01277       /* We are heading for another depot, keep driving. */
    01278       return;
    01279     }
    01280
    01281     if (t.IsRefit()) {
    01282       Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
    01283       CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
    01284       cur_company.Restore();
    01285
    01286       if (cost.Failed()) {
    01287         _vehicles_to_autoreplace[v] = false;
    01288         if (v->owner == _local_company) {
    01289           /* Notify the user that we stopped the vehicle */
    01290           SetDParam(0, v->index);
    01291           AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
    01292         }
    01293       } else if (cost.GetCost() != 0) {
    01294         v->profit_this_year -= cost.GetCost() << 8;
    01295         if (v->owner == _local_company) {
    01296           ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
    01297         }
    01298       }
    01299     }
    01300
    01301     if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
    01302       /* Part of orders */
    01303       v->DeleteUnreachedAutoOrders();
    01304       UpdateVehicleTimetable(v, true);
    01305       v->IncrementAutoOrderIndex();
    01306     }
    01307     if (t.GetDepotActionType() & ODATFB_HALT) {
    01308       /* Vehicles are always stopped on entering depots. Do not restart this one. */
    01309       _vehicles_to_autoreplace[v] = false;
    01310       if (v->owner == _local_company) {
    01311         SetDParam(0, v->index);
    01312         AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
    01313       }
    01314       AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
    01315     }
    01316   }
    01317 }
    01318
    01319
    01327 void VehicleMove(Vehicle *v, bool update_viewport)
    01328 {
    01329   int img = v->cur_image;
    01330   Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
    01331   const Sprite *spr = GetSprite(img, ST_NORMAL);
    01332
    01333   pt.x += spr->x_offs;
    01334   pt.y += spr->y_offs;
    01335
    01336   UpdateVehiclePosHash(v, pt.x, pt.y);
    01337
    01338   Rect old_coord = v->coord;
    01339   v->coord.left   = pt.x;
    01340   v->coord.top    = pt.y;
    01341   v->coord.right  = pt.x + spr->width + 2;
    01342   v->coord.bottom = pt.y + spr->height + 2;
    01343
    01344   if (update_viewport) {
    01345     MarkAllViewportsDirty(
    01346       min(old_coord.left,   v->coord.left),
    01347       min(old_coord.top,    v->coord.top),
    01348       max(old_coord.right,  v->coord.right) + 1,
    01349       max(old_coord.bottom, v->coord.bottom) + 1
    01350     );
    01351   }
    01352 }
    01353
    01362 void MarkSingleVehicleDirty(const Vehicle *v)
    01363 {
    01364   MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
    01365 }
    01366
    01372 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
    01373 {
    01374   static const int8 _delta_coord[16] = {
    01375     -1,-1,-1, 0, 1, 1, 1, 0, /* x */
    01376     -1, 0, 1, 1, 1, 0,-1,-1, /* y */
    01377   };
    01378
    01379   int x = v->x_pos + _delta_coord[v->direction];
    01380   int y = v->y_pos + _delta_coord[v->direction + 8];
    01381
    01382   GetNewVehiclePosResult gp;
    01383   gp.x = x;
    01384   gp.y = y;
    01385   gp.old_tile = v->tile;
    01386   gp.new_tile = TileVirtXY(x, y);
    01387   return gp;
    01388 }
    01389
    01390 static const Direction _new_direction_table[] = {
    01391   DIR_N,  DIR_NW, DIR_W,
    01392   DIR_NE, DIR_SE, DIR_SW,
    01393   DIR_E,  DIR_SE, DIR_S
    01394 };
    01395
    01396 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
    01397 {
    01398   int i = 0;
    01399
    01400   if (y >= v->y_pos) {
    01401     if (y != v->y_pos) i += 3;
    01402     i += 3;
    01403   }
    01404
    01405   if (x >= v->x_pos) {
    01406     if (x != v->x_pos) i++;
    01407     i++;
    01408   }
    01409
    01410   Direction dir = v->direction;
    01411
    01412   DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
    01413   if (dirdiff == DIRDIFF_SAME) return dir;
    01414   return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
    01415 }
    01416
    01426 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
    01427 {
    01428   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
    01429 }
    01430
    01438 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
    01439 {
    01440   /* Find maximum */
    01441   const Vehicle *v;
    01442   FOR_ALL_VEHICLES(v) {
    01443     if (v->type == type && v->owner == owner) {
    01444       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
    01445     }
    01446   }
    01447
    01448   if (this->maxid == 0) return;
    01449
    01450   /* Reserving 'maxid + 2' because we need:
    01451    * - space for the last item (with v->unitnumber == maxid)
    01452    * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
    01453   this->cache = CallocT<bool>(this->maxid + 2);
    01454
    01455   /* Fill the cache */
    01456   FOR_ALL_VEHICLES(v) {
    01457     if (v->type == type && v->owner == owner) {
    01458       this->cache[v->unitnumber] = true;
    01459     }
    01460   }
    01461 }
    01462
    01464 UnitID FreeUnitIDGenerator::NextID()
    01465 {
    01466   if (this->maxid <= this->curid) return ++this->curid;
    01467
    01468   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
    01469
    01470   return this->curid;
    01471 }
    01472
    01478 UnitID GetFreeUnitNumber(VehicleType type)
    01479 {
    01480   /* Check whether it is allowed to build another vehicle. */
    01481   uint max_veh;
    01482   switch (type) {
    01483     case VEH_TRAIN:    max_veh = _settings_game.vehicle.max_trains;   break;
    01484     case VEH_ROAD:     max_veh = _settings_game.vehicle.max_roadveh;  break;
    01485     case VEH_SHIP:     max_veh = _settings_game.vehicle.max_ships;    break;
    01486     case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
    01487     default: NOT_REACHED();
    01488   }
    01489
    01490   uint amounts[4];
    01491   CountCompanyVehicles(_current_company, amounts);
    01492   assert((uint)type < lengthof(amounts));
    01493   if (amounts[type] >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
    01494
    01495   FreeUnitIDGenerator gen(type, _current_company);
    01496
    01497   return gen.NextID();
    01498 }
    01499
    01500
    01509 bool CanBuildVehicleInfrastructure(VehicleType type)
    01510 {
    01511   assert(IsCompanyBuildableVehicleType(type));
    01512
    01513   if (!Company::IsValidID(_local_company)) return false;
    01514   if (!_settings_client.gui.disable_unsuitable_building) return true;
    01515
    01516   UnitID max;
    01517   switch (type) {
    01518     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
    01519     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
    01520     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
    01521     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
    01522     default: NOT_REACHED();
    01523   }
    01524
    01525   /* We can build vehicle infrastructure when we may build the vehicle type */
    01526   if (max > 0) {
    01527     /* Can we actually build the vehicle type? */
    01528     const Engine *e;
    01529     FOR_ALL_ENGINES_OF_TYPE(e, type) {
    01530       if (HasBit(e->company_avail, _local_company)) return true;
    01531     }
    01532     return false;
    01533   }
    01534
    01535   /* We should be able to build infrastructure when we have the actual vehicle type */
    01536   const Vehicle *v;
    01537   FOR_ALL_VEHICLES(v) {
    01538     if (v->owner == _local_company && v->type == type) return true;
    01539   }
    01540
    01541   return false;
    01542 }
    01543
    01544
    01552 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
    01553 {
    01554   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
    01555   const Engine *e = Engine::Get(engine_type);
    01556   switch (e->type) {
    01557     default: NOT_REACHED();
    01558     case VEH_TRAIN:
    01559       if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
    01560         /* Wagonoverrides use the colour scheme of the front engine.
    01561          * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
    01562         engine_type = parent_engine_type;
    01563         e = Engine::Get(engine_type);
    01564         /* Note: Luckily cargo_type is not needed for engines */
    01565       }
    01566
    01567       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01568       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01569       if (e->u.rail.railveh_type == RAILVEH_WAGON) {
    01570         if (!CargoSpec::Get(cargo_type)->is_freight) {
    01571           if (parent_engine_type == INVALID_ENGINE) {
    01572             return LS_PASSENGER_WAGON_STEAM;
    01573           } else {
    01574             switch (RailVehInfo(parent_engine_type)->engclass) {
    01575               default: NOT_REACHED();
    01576               case EC_STEAM:    return LS_PASSENGER_WAGON_STEAM;
    01577               case EC_DIESEL:   return LS_PASSENGER_WAGON_DIESEL;
    01578               case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
    01579               case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
    01580               case EC_MAGLEV:   return LS_PASSENGER_WAGON_MAGLEV;
    01581             }
    01582           }
    01583         } else {
    01584           return LS_FREIGHT_WAGON;
    01585         }
    01586       } else {
    01587         bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
    01588
    01589         switch (e->u.rail.engclass) {
    01590           default: NOT_REACHED();
    01591           case EC_STEAM:    return LS_STEAM;
    01592           case EC_DIESEL:   return is_mu ? LS_DMU : LS_DIESEL;
    01593           case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
    01594           case EC_MONORAIL: return LS_MONORAIL;
    01595           case EC_MAGLEV:   return LS_MAGLEV;
    01596         }
    01597       }
    01598
    01599     case VEH_ROAD:
    01600       /* Always use the livery of the front */
    01601       if (v != NULL && parent_engine_type != INVALID_ENGINE) {
    01602         engine_type = parent_engine_type;
    01603         e = Engine::Get(engine_type);
    01604         cargo_type = v->First()->cargo_type;
    01605       }
    01606       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01607       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01608
    01609       /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
    01610       if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
    01611         /* Tram */
    01612         return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
    01613       } else {
    01614         /* Bus or truck */
    01615         return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
    01616       }
    01617
    01618     case VEH_SHIP:
    01619       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01620       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01621       return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
    01622
    01623     case VEH_AIRCRAFT:
    01624       switch (e->u.air.subtype) {
    01625         case AIR_HELI: return LS_HELICOPTER;
    01626         case AIR_CTOL: return LS_SMALL_PLANE;
    01627         case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
    01628         default: NOT_REACHED();
    01629       }
    01630   }
    01631 }
    01632
    01642 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
    01643 {
    01644   const Company *c = Company::Get(company);
    01645   LiveryScheme scheme = LS_DEFAULT;
    01646
    01647   /* The default livery is always available for use, but its in_use flag determines
    01648    * whether any _other_ liveries are in use. */
    01649   if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
    01650     /* Determine the livery scheme to use */
    01651     scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
    01652
    01653     /* Switch back to the default scheme if the resolved scheme is not in use */
    01654     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
    01655   }
    01656
    01657   return &c->livery[scheme];
    01658 }
    01659
    01660
    01661 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
    01662 {
    01663   PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
    01664
    01665   /* Return cached value if any */
    01666   if (map != PAL_NONE) return map;
    01667
    01668   const Engine *e = Engine::Get(engine_type);
    01669
    01670   /* Check if we should use the colour map callback */
    01671   if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
    01672     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
    01673     /* Failure means "use the default two-colour" */
    01674     if (callback != CALLBACK_FAILED) {
    01675       assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) conincidences with default value (PAL_NONE)
    01676       map = GB(callback, 0, 14);
    01677       /* If bit 14 is set, then the company colours are applied to the
    01678        * map else it's returned as-is. */
    01679       if (!HasBit(callback, 14)) {
    01680         /* Update cache */
    01681         if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
    01682         return map;
    01683       }
    01684     }
    01685   }
    01686
    01687   bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
    01688
    01689   if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
    01690
    01691   /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
    01692   if (!Company::IsValidID(company)) return map;
    01693
    01694   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
    01695
    01696   map += livery->colour1;
    01697   if (twocc) map += livery->colour2 * 16;
    01698
    01699   /* Update cache */
    01700   if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
    01701   return map;
    01702 }
    01703
    01710 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
    01711 {
    01712   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
    01713 }
    01714
    01720 PaletteID GetVehiclePalette(const Vehicle *v)
    01721 {
    01722   if (v->IsGroundVehicle()) {
    01723     return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
    01724   }
    01725
    01726   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
    01727 }
    01728
    01737 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
    01738 {
    01739   if (mail_capacity != NULL) *mail_capacity = 0;
    01740   const Engine *e = Engine::Get(v->engine_type);
    01741
    01742   if (!e->CanCarryCargo()) return 0;
    01743
    01744   if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
    01745     *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
    01746   }
    01747   CargoID default_cargo = e->GetDefaultCargoType();
    01748
    01749   /* Check the refit capacity callback if we are not in the default configuration.
    01750    * Note: This might change to become more consistent/flexible/sane, esp. when default cargo is first refittable. */
    01751   if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
    01752       (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
    01753     uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
    01754     if (callback != CALLBACK_FAILED) return callback;
    01755   }
    01756
    01757   /* Get capacity according to property resp. CB */
    01758   uint capacity;
    01759   switch (e->type) {
    01760     case VEH_TRAIN:    capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY,        e->u.rail.capacity); break;
    01761     case VEH_ROAD:     capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY,      e->u.road.capacity); break;
    01762     case VEH_SHIP:     capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY,         e->u.ship.capacity); break;
    01763     case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
    01764     default: NOT_REACHED();
    01765   }
    01766
    01767   /* Apply multipliers depending on cargo- and vehicletype.
    01768    * Note: This might change to become more consistent/flexible. */
    01769   if (e->type != VEH_SHIP) {
    01770     if (e->type == VEH_AIRCRAFT) {
    01771       if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
    01772         capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
    01773       }
    01774       if (v->cargo_type == CT_MAIL) return capacity;
    01775     } else {
    01776       switch (default_cargo) {
    01777         case CT_PASSENGERS: break;
    01778         case CT_MAIL:
    01779         case CT_GOODS: capacity *= 2; break;
    01780         default:       capacity *= 4; break;
    01781       }
    01782     }
    01783     switch (v->cargo_type) {
    01784       case CT_PASSENGERS: break;
    01785       case CT_MAIL:
    01786       case CT_GOODS: capacity /= 2; break;
    01787       default:       capacity /= 4; break;
    01788     }
    01789   }
    01790
    01791   return capacity;
    01792 }
    01793
    01797 void Vehicle::DeleteUnreachedAutoOrders()
    01798 {
    01799   const Order *order = this->GetOrder(this->cur_auto_order_index);
    01800   while (order != NULL) {
    01801     if (this->cur_auto_order_index == this->cur_real_order_index) break;
    01802
    01803     if (order->IsType(OT_AUTOMATIC)) {
    01804       /* Delete order effectively deletes order, so get the next before deleting it. */
    01805       order = order->next;
    01806       DeleteOrder(this, this->cur_auto_order_index);
    01807     } else {
    01808       /* Skip non-automatic orders, e.g. service-orders */
    01809       order = order->next;
    01810       this->cur_auto_order_index++;
    01811     }
    01812
    01813     /* Wrap around */
    01814     if (order == NULL) {
    01815       order = this->GetOrder(0);
    01816       this->cur_auto_order_index = 0;
    01817     }
    01818   }
    01819 }
    01820
    01825 void Vehicle::BeginLoading()
    01826 {
    01827   assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
    01828
    01829   if (this->current_order.IsType(OT_GOTO_STATION) &&
    01830       this->current_order.GetDestination() == this->last_station_visited) {
    01831     this->DeleteUnreachedAutoOrders();
    01832
    01833     /* Now both order indices point to the destination station, and we can start loading */
    01834     this->current_order.MakeLoading(true);
    01835     UpdateVehicleTimetable(this, true);
    01836
    01837     /* Furthermore add the Non Stop flag to mark that this station
    01838      * is the actual destination of the vehicle, which is (for example)
    01839      * necessary to be known for HandleTrainLoading to determine
    01840      * whether the train is lost or not; not marking a train lost
    01841      * that arrives at random stations is bad. */
    01842     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
    01843
    01844   } else {
    01845     /* We weren't scheduled to stop here. Insert an automatic order
    01846      * to show that we are stopping here, but only do that if the order
    01847      * list isn't empty. */
    01848     Order *in_list = this->GetOrder(this->cur_auto_order_index);
    01849     if (in_list != NULL && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID &&
    01850         (!in_list->IsType(OT_AUTOMATIC) ||
    01851         in_list->GetDestination() != this->last_station_visited) &&
    01852         Order::CanAllocateItem()) {
    01853       Order *auto_order = new Order();
    01854       auto_order->MakeAutomatic(this->last_station_visited);
    01855       InsertOrder(this, auto_order, this->cur_auto_order_index);
    01856       if (this->cur_auto_order_index > 0) --this->cur_auto_order_index;
    01857     }
    01858     this->current_order.MakeLoading(false);
    01859   }
    01860
    01861   Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
    01862
    01863   PrepareUnload(this);
    01864
    01865   SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
    01866   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01867   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
    01868   SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
    01869
    01870   Station::Get(this->last_station_visited)->MarkTilesDirty(true);
    01871   this->cur_speed = 0;
    01872   this->MarkDirty();
    01873 }
    01874
    01879 void Vehicle::LeaveStation()
    01880 {
    01881   assert(this->current_order.IsType(OT_LOADING));
    01882
    01883   delete this->cargo_payment;
    01884
    01885   /* Only update the timetable if the vehicle was supposed to stop here. */
    01886   if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
    01887
    01888   this->current_order.MakeLeaveStation();
    01889   Station *st = Station::Get(this->last_station_visited);
    01890   st->loading_vehicles.remove(this);
    01891
    01892   HideFillingPercent(&this->fill_percent_te_id);
    01893
    01894   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
    01895     /* Trigger station animation (trains only) */
    01896     if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
    01897
    01898     SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
    01899   }
    01900 }
    01901
    01902
    01908 void Vehicle::HandleLoading(bool mode)
    01909 {
    01910   switch (this->current_order.GetType()) {
    01911     case OT_LOADING: {
    01912       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
    01913
    01914       /* Not the first call for this tick, or still loading */
    01915       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
    01916
    01917       this->PlayLeaveStationSound();
    01918
    01919       this->LeaveStation();
    01920
    01921       break;
    01922     }
    01923
    01924     case OT_DUMMY: break;
    01925
    01926     default: return;
    01927   }
    01928
    01929   this->IncrementAutoOrderIndex();
    01930 }
    01931
    01938 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
    01939 {
    01940   CommandCost ret = CheckOwnership(this->owner);
    01941   if (ret.Failed()) return ret;
    01942
    01943   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
    01944   if (this->IsStoppedInDepot()) return CMD_ERROR;
    01945
    01946   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
    01947     bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
    01948     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
    01949       /* We called with a different DEPOT_SERVICE setting.
    01950        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
    01951        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
    01952       if (flags & DC_EXEC) {
    01953         this->current_order.SetDepotOrderType(ODTF_MANUAL);
    01954         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
    01955         SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01956       }
    01957       return CommandCost();
    01958     }
    01959
    01960     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
    01961     if (flags & DC_EXEC) {
    01962       /* If the orders to 'goto depot' are in the orders list (forced servicing),
    01963        * then skip to the next order; effectively cancelling this forced service */
    01964       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
    01965
    01966       this->current_order.MakeDummy();
    01967       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01968     }
    01969     return CommandCost();
    01970   }
    01971
    01972   TileIndex location;
    01973   DestinationID destination;
    01974   bool reverse;
    01975   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};
    01976   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
    01977
    01978   if (flags & DC_EXEC) {
    01979     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
    01980
    01981     this->dest_tile = location;
    01982     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
    01983     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
    01984     SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01985
    01986     /* If there is no depot in front, reverse automatically (trains only) */
    01987     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
    01988
    01989     if (this->type == VEH_AIRCRAFT) {
    01990       Aircraft *a = Aircraft::From(this);
    01991       if (a->state == FLYING && a->targetairport != destination) {
    01992         /* The aircraft is now heading for a different hangar than the next in the orders */
    01993         extern void AircraftNextAirportPos_and_Order(Aircraft *a);
    01994         AircraftNextAirportPos_and_Order(a);
    01995       }
    01996     }
    01997   }
    01998
    01999   return CommandCost();
    02000
    02001 }
    02002
    02007 void Vehicle::UpdateVisualEffect(bool allow_power_change)
    02008 {
    02009   bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
    02010   const Engine *e = Engine::Get(this->engine_type);
    02011
    02012   /* Evaluate properties */
    02013   byte visual_effect;
    02014   switch (e->type) {
    02015     case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
    02016     case VEH_ROAD:  visual_effect = e->u.road.visual_effect; break;
    02017     case VEH_SHIP:  visual_effect = e->u.ship.visual_effect; break;
    02018     default:        visual_effect = 1 << VE_DISABLE_EFFECT;  break;
    02019   }
    02020
    02021   /* Check powered wagon / visual effect callback */
    02022   if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
    02023     uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
    02024
    02025     if (callback != CALLBACK_FAILED) {
    02026       callback = GB(callback, 0, 8);
    02027       /* Avoid accidentally setting 'visual_effect' to the default value
    02028        * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
    02029       if (callback == VE_DEFAULT) {
    02030         assert(HasBit(callback, VE_DISABLE_EFFECT));
    02031         SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
    02032       }
    02033       visual_effect = callback;
    02034     }
    02035   }
    02036
    02037   /* Apply default values */
    02038   if (visual_effect == VE_DEFAULT ||
    02039       (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
    02040     /* Only train engines have default effects.
    02041      * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
    02042     if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
    02043       if (visual_effect == VE_DEFAULT) {
    02044         visual_effect = 1 << VE_DISABLE_EFFECT;
    02045       } else {
    02046         SetBit(visual_effect, VE_DISABLE_EFFECT);
    02047       }
    02048     } else {
    02049       if (visual_effect == VE_DEFAULT) {
    02050         /* Also set the offset */
    02051         visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
    02052       }
    02053       SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
    02054     }
    02055   }
    02056
    02057   this->vcache.cached_vis_effect = visual_effect;
    02058
    02059   if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
    02060     ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
    02061     ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
    02062   }
    02063 }
    02064
    02065 static const int8 _vehicle_smoke_pos[8] = {
    02066   1, 1, 1, 0, -1, -1, -1, 0
    02067 };
    02068
    02073 void Vehicle::ShowVisualEffect() const
    02074 {
    02075   assert(this->IsPrimaryVehicle());
    02076   bool sound = false;
    02077
    02078   /* Do not show any smoke when:
    02079    * - vehicle smoke is disabled by the player
    02080    * - the vehicle is slowing down or stopped (by the player)
    02081    * - the vehicle is moving very slowly
    02082    */
    02083   if (_settings_game.vehicle.smoke_amount == 0 ||
    02084       this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
    02085       this->cur_speed < 2) {
    02086     return;
    02087   }
    02088   if (this->type == VEH_TRAIN) {
    02089     const Train *t = Train::From(this);
    02090     /* For trains, do not show any smoke when:
    02091      * - the train is reversing
    02092      * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
    02093      */
    02094     if (HasBit(t->flags, VRF_REVERSING) ||
    02095         (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
    02096         t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
    02097       return;
    02098     }
    02099   }
    02100
    02101   const Vehicle *v = this;
    02102
    02103   do {
    02104     int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
    02105     byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
    02106     bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
    02107
    02108     /* Show no smoke when:
    02109      * - Smoke has been disabled for this vehicle
    02110      * - The vehicle is not visible
    02111      * - The vehicle is under a bridge
    02112      * - The vehicle is on a depot tile
    02113      * - The vehicle is on a tunnel tile
    02114      * - The vehicle is a train engine that is currently unpowered */
    02115     if (disable_effect ||
    02116         v->vehstatus & VS_HIDDEN ||
    02117         (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
    02118         IsDepotTile(v->tile) ||
    02119         IsTunnelTile(v->tile) ||
    02120         (v->type == VEH_TRAIN &&
    02121         !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
    02122       continue;
    02123     }
    02124
    02125     int x = _vehicle_smoke_pos[v->direction] * effect_offset;
    02126     int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
    02127
    02128     if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
    02129       x = -x;
    02130       y = -y;
    02131     }
    02132
    02133     switch (effect_type) {
    02134       case VE_TYPE_STEAM:
    02135         /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
    02136          * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
    02137          * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
    02138          * REGULATION:
    02139          * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
    02140         if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
    02141           CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
    02142           sound = true;
    02143         }
    02144         break;
    02145
    02146       case VE_TYPE_DIESEL: {
    02147         /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
    02148          * when smoke emission stops.
    02149          * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
    02150          * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
    02151          * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
    02152          * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
    02153          * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
    02154          * maximum speed no diesel_smoke is emitted.
    02155          * REGULATION:
    02156          * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
    02157          * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
    02158         int power_weight_effect = 0;
    02159         if (v->type == VEH_TRAIN) {
    02160           power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
    02161         }
    02162         if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
    02163             Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
    02164           CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
    02165           sound = true;
    02166         }
    02167         break;
    02168       }
    02169
    02170       case VE_TYPE_ELECTRIC:
    02171         /* Electric train's spark - more often occurs when train is departing (more load)
    02172          * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
    02173          * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
    02174          * reaching its max. speed, quarter by quarter of it, chance decreases untill the usuall 2,22% at train's top speed.
    02175          * REGULATION:
    02176          * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
    02177         if (GB(v->tick_counter, 0, 2) == 0 &&
    02178             Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
    02179           CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
    02180           sound = true;
    02181         }
    02182         break;
    02183
    02184       default:
    02185         break;
    02186     }
    02187   } while ((v = v->Next()) != NULL);
    02188
    02189   if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
    02190 }
    02191
    02196 void Vehicle::SetNext(Vehicle *next)
    02197 {
    02198   assert(this != next);
    02199
    02200   if (this->next != NULL) {
    02201     /* We had an old next vehicle. Update the first and previous pointers */
    02202     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    02203       v->first = this->next;
    02204     }
    02205     this->next->previous = NULL;
    02206   }
    02207
    02208   this->next = next;
    02209
    02210   if (this->next != NULL) {
    02211     /* A new next vehicle. Update the first and previous pointers */
    02212     if (this->next->previous != NULL) this->next->previous->next = NULL;
    02213     this->next->previous = this;
    02214     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    02215       v->first = this->first;
    02216     }
    02217   }
    02218 }
    02219
    02225 void Vehicle::AddToShared(Vehicle *shared_chain)
    02226 {
    02227   assert(this->previous_shared == NULL && this->next_shared == NULL);
    02228
    02229   if (shared_chain->orders.list == NULL) {
    02230     assert(shared_chain->previous_shared == NULL);
    02231     assert(shared_chain->next_shared == NULL);
    02232     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
    02233   }
    02234
    02235   this->next_shared     = shared_chain->next_shared;
    02236   this->previous_shared = shared_chain;
    02237
    02238   shared_chain->next_shared = this;
    02239
    02240   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
    02241
    02242   shared_chain->orders.list->AddVehicle(this);
    02243 }
    02244
    02248 void Vehicle::RemoveFromShared()
    02249 {
    02250   /* Remember if we were first and the old window number before RemoveVehicle()
    02251    * as this changes first if needed. */
    02252   bool were_first = (this->FirstShared() == this);
    02253   VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
    02254
    02255   this->orders.list->RemoveVehicle(this);
    02256
    02257   if (!were_first) {
    02258     /* We are not the first shared one, so only relink our previous one. */
    02259     this->previous_shared->next_shared = this->NextShared();
    02260   }
    02261
    02262   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
    02263
    02264
    02265   if (this->orders.list->GetNumVehicles() == 1) {
    02266     /* When there is only one vehicle, remove the shared order list window. */
    02267     DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
    02268     InvalidateVehicleOrder(this->FirstShared(), 0);
    02269   } else if (were_first) {
    02270     /* If we were the first one, update to the new first one.
    02271      * Note: FirstShared() is already the new first */
    02272     InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
    02273   }
    02274
    02275   this->next_shared     = NULL;
    02276   this->previous_shared = NULL;
    02277 }
    02278
    02279 void VehiclesYearlyLoop()
    02280 {
    02281   Vehicle *v;
    02282   FOR_ALL_VEHICLES(v) {
    02283     if (v->IsPrimaryVehicle()) {
    02284       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
    02285       Money profit = v->GetDisplayProfitThisYear();
    02286       if (v->age >= 730 && profit < 0) {
    02287         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
    02288           SetDParam(0, v->index);
    02289           SetDParam(1, profit);
    02290           AddVehicleNewsItem(
    02291             STR_NEWS_VEHICLE_IS_UNPROFITABLE,
    02292             NS_ADVICE,
    02293             v->index
    02294           );
    02295         }
    02296         AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
    02297       }
    02298
    02299       v->profit_last_year = v->profit_this_year;
    02300       v->profit_this_year = 0;
    02301       SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    02302     }
    02303   }
    02304 }
    02305
    02306
    02316 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
    02317 {
    02318   const Engine *e = Engine::GetIfValid(engine_type);
    02319   assert(e != NULL);
    02320
    02321   switch (e->type) {
    02322     case VEH_TRAIN:
    02323       return (st->facilities & FACIL_TRAIN) != 0;
    02324
    02325     case VEH_ROAD:
    02326       /* For road vehicles we need the vehicle to know whether it can actually
    02327        * use the station, but if it doesn't have facilities for RVs it is
    02328        * certainly not possible that the station can be used. */
    02329       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
    02330
    02331     case VEH_SHIP:
    02332       return (st->facilities & FACIL_DOCK) != 0;
    02333
    02334     case VEH_AIRCRAFT:
    02335       return (st->facilities & FACIL_AIRPORT) != 0 &&
    02336           (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
    02337
    02338     default:
    02339       return false;
    02340   }
    02341 }
    02342
    02349 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
    02350 {
    02351   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
    02352
    02353   return CanVehicleUseStation(v->engine_type, st);
    02354 }
    02355
    02361 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
    02362 {
    02363   assert(this->IsGroundVehicle());
    02364   if (this->type == VEH_TRAIN) {
    02365     return &Train::From(this)->gcache;
    02366   } else {
    02367     return &RoadVehicle::From(this)->gcache;
    02368   }
    02369 }
    02370
    02376 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
    02377 {
    02378   assert(this->IsGroundVehicle());
    02379   if (this->type == VEH_TRAIN) {
    02380     return &Train::From(this)->gcache;
    02381   } else {
    02382     return &RoadVehicle::From(this)->gcache;
    02383   }
    02384 }
    02385
    02394 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
    02395 {
    02396   if (v->type == VEH_TRAIN) {
    02397     Train *u = Train::From(v);
    02398     /* Only include whole vehicles, so start with the first articulated part */
    02399     u = u->GetFirstEnginePart();
    02400
    02401     /* Include num_vehicles vehicles, not counting articulated parts */
    02402     for (; u != NULL && num_vehicles > 0; num_vehicles--) {
    02403       do {
    02404         /* Include current vehicle in the selection. */
    02405         set.Include(u->index);
    02406
    02407         /* If the vehicle is multiheaded, add the other part too. */
    02408         if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
    02409
    02410         u = u->Next();
    02411       } while (u != NULL && u->IsArticulatedPart());
    02412     }
    02413   }
    02414 }