# vehicle.cpp

Go to the documentation of this file.

    00001 /* $Id: vehicle.cpp 17598 2009-09-21 15:41:58Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "gui.h"
    00007 #include "openttd.h"
    00008 #include "debug.h"
    00009 #include "roadveh.h"
    00010 #include "ship.h"
    00011 #include "spritecache.h"
    00012 #include "landscape.h"
    00013 #include "timetable.h"
    00014 #include "viewport_func.h"
    00015 #include "news_func.h"
    00016 #include "command_func.h"
    00017 #include "company_func.h"
    00018 #include "vehicle_gui.h"
    00019 #include "train.h"
    00020 #include "aircraft.h"
    00021 #include "newgrf_engine.h"
    00022 #include "newgrf_sound.h"
    00023 #include "newgrf_station.h"
    00024 #include "group.h"
    00025 #include "group_gui.h"
    00026 #include "strings_func.h"
    00027 #include "zoom_func.h"
    00028 #include "functions.h"
    00029 #include "date_func.h"
    00030 #include "window_func.h"
    00031 #include "vehicle_func.h"
    00032 #include "autoreplace_func.h"
    00033 #include "autoreplace_gui.h"
    00034 #include "oldpool_func.h"
    00035 #include "ai/ai.hpp"
    00036 #include "core/smallmap_type.hpp"
    00037 #include "depot_func.h"
    00038 #include "settings_type.h"
    00039 #include "network/network.h"
    00040
    00041 #include "economy_base.h"
    00042 #include "table/sprites.h"
    00043 #include "table/strings.h"
    00044
    00045 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
    00046
    00047 VehicleID _vehicle_id_ctr_day;
    00048 const Vehicle *_place_clicked_vehicle;
    00049 VehicleID _new_vehicle_id;
    00050 uint16 _returned_refit_capacity;
    00051
    00052
    00053 /* Initialize the vehicle-pool */
    00054 DEFINE_OLD_POOL_GENERIC(Vehicle, Vehicle)
    00055
    00056
    00060 bool Vehicle::NeedsAutorenewing(const Company *c) const
    00061 {
    00062   /* We can always generate the Company pointer when we have the vehicle.
    00063    * However this takes time and since the Company pointer is often present
    00064    * when this function is called then it's faster to pass the pointer as an
    00065    * argument rather than finding it again. */
    00066   assert(c == GetCompany(this->owner));
    00067
    00068   if (!c->engine_renew) return false;
    00069   if (this->age - this->max_age < (c->engine_renew_months * 30)) return false;
    00070   if (this->age == 0) return false; // rail cars don't age and lacks a max age
    00071
    00072   return true;
    00073 }
    00074
    00075 void VehicleServiceInDepot(Vehicle *v)
    00076 {
    00077   v->date_of_last_service = _date;
    00078   v->breakdowns_since_last_service = 0;
    00079   v->reliability = GetEngine(v->engine_type)->reliability;
    00080   InvalidateWindow(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
    00081 }
    00082
    00083 bool Vehicle::NeedsServicing() const
    00084 {
    00085   if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
    00086
    00087   if (_settings_game.order.no_servicing_if_no_breakdowns && _settings_game.difficulty.vehicle_breakdowns == 0) {
    00088     /* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off.
    00089      * Note: If servicing is enabled, we postpone replacement till next service. */
    00090     return EngineHasReplacementForCompany(GetCompany(this->owner), this->engine_type, this->group_id);
    00091   }
    00092
    00093   return _settings_game.vehicle.servint_ispercent ?
    00094     (this->reliability < GetEngine(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
    00095     (this->date_of_last_service + this->service_interval < _date);
    00096 }
    00097
    00098 bool Vehicle::NeedsAutomaticServicing() const
    00099 {
    00100   if (_settings_game.order.gotodepot && VehicleHasDepotOrders(this)) return false;
    00101   if (this->current_order.IsType(OT_LOADING))            return false;
    00102   if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
    00103   return NeedsServicing();
    00104 }
    00105
    00114 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
    00115 {
    00116   const Engine *e = GetEngine(engine);
    00117   uint32 grfid = e->grffile->grfid;
    00118   GRFConfig *grfconfig = GetGRFConfig(grfid);
    00119
    00120   if (!HasBit(grfconfig->grf_bugs, bug_type)) {
    00121     SetBit(grfconfig->grf_bugs, bug_type);
    00122     SetDParamStr(0, grfconfig->name);
    00123     SetDParam(1, engine);
    00124     ShowErrorMessage(part2, part1, 0, 0);
    00125     if (!_networking) _pause_game = (critical ? -1 : 1);
    00126   }
    00127
    00128   /* debug output */
    00129   char buffer[512];
    00130
    00131   SetDParamStr(0, grfconfig->name);
    00132   GetString(buffer, part1, lastof(buffer));
    00133   DEBUG(grf, 0, "%s", buffer + 3);
    00134
    00135   SetDParam(1, engine);
    00136   GetString(buffer, part2, lastof(buffer));
    00137   DEBUG(grf, 0, "%s", buffer + 3);
    00138 }
    00139
    00140 StringID VehicleInTheWayErrMsg(const Vehicle *v)
    00141 {
    00142   switch (v->type) {
    00143     case VEH_TRAIN:    return STR_8803_TRAIN_IN_THE_WAY;
    00144     case VEH_ROAD:     return STR_9000_ROAD_VEHICLE_IN_THE_WAY;
    00145     case VEH_AIRCRAFT: return STR_A015_AIRCRAFT_IN_THE_WAY;
    00146     default:           return STR_980E_SHIP_IN_THE_WAY;
    00147   }
    00148 }
    00149
    00150 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
    00151 {
    00152   byte z = *(byte*)data;
    00153
    00154   if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
    00155   if (v->z_pos > z) return NULL;
    00156
    00157   _error_message = VehicleInTheWayErrMsg(v);
    00158   return v;
    00159 }
    00160
    00161 bool EnsureNoVehicleOnGround(TileIndex tile)
    00162 {
    00163   byte z = GetTileMaxZ(tile);
    00164   return !HasVehicleOnPos(tile, &z, &EnsureNoVehicleProcZ);
    00165 }
    00166
    00168 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
    00169 {
    00170   if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
    00171   if (v == (const Vehicle *)data) return NULL;
    00172
    00173   _error_message = VehicleInTheWayErrMsg(v);
    00174   return v;
    00175 }
    00176
    00184 bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
    00185 {
    00186   return HasVehicleOnPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc) ||
    00187       HasVehicleOnPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc);
    00188 }
    00189
    00190
    00191 Vehicle::Vehicle()
    00192 {
    00193   this->type               = VEH_INVALID;
    00194   this->coord.left         = INVALID_COORD;
    00195   this->group_id           = DEFAULT_GROUP;
    00196   this->fill_percent_te_id = INVALID_TE_ID;
    00197   this->first              = this;
    00198   this->colourmap          = PAL_NONE;
    00199 }
    00200
    00205 byte VehicleRandomBits()
    00206 {
    00207   return GB(Random(), 0, 8);
    00208 }
    00209
    00210
    00211 /* static */ bool Vehicle::AllocateList(Vehicle **vl, int num)
    00212 {
    00213   if (!Vehicle::CanAllocateItem(num)) return false;
    00214   if (vl == NULL) return true;
    00215
    00216   uint counter = _Vehicle_pool.first_free_index;
    00217
    00218   for (int i = 0; i != num; i++) {
    00219     vl[i] = new (AllocateRaw(counter)) InvalidVehicle();
    00220     counter++;
    00221   }
    00222
    00223   return true;
    00224 }
    00225
    00226 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
    00227  * lookup times at the expense of memory usage. */
    00228 const int HASH_BITS = 7;
    00229 const int HASH_SIZE = 1 << HASH_BITS;
    00230 const int HASH_MASK = HASH_SIZE - 1;
    00231 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
    00232 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
    00233
    00234 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
    00235  * Profiling results show that 0 is fastest. */
    00236 const int HASH_RES = 0;
    00237
    00238 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
    00239
    00240 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
    00241 {
    00242   for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
    00243     for (int x = xl; ; x = (x + 1) & HASH_MASK) {
    00244       Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
    00245       for (; v != NULL; v = v->next_new_hash) {
    00246         Vehicle *a = proc(v, data);
    00247         if (find_first && a != NULL) return a;
    00248       }
    00249       if (x == xu) break;
    00250     }
    00251     if (y == yu) break;
    00252   }
    00253
    00254   return NULL;
    00255 }
    00256
    00257
    00269 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
    00270 {
    00271   const int COLL_DIST = 6;
    00272
    00273   /* Hash area to scan is from xl,yl to xu,yu */
    00274   int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
    00275   int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
    00276   int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
    00277   int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
    00278
    00279   return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
    00280 }
    00281
    00296 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
    00297 {
    00298   VehicleFromPosXY(x, y, data, proc, false);
    00299 }
    00300
    00312 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
    00313 {
    00314   return VehicleFromPosXY(x, y, data, proc, true) != NULL;
    00315 }
    00316
    00327 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
    00328 {
    00329   int x = GB(TileX(tile), HASH_RES, HASH_BITS);
    00330   int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
    00331
    00332   Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
    00333   for (; v != NULL; v = v->next_new_hash) {
    00334     if (v->tile != tile) continue;
    00335
    00336     Vehicle *a = proc(v, data);
    00337     if (find_first && a != NULL) return a;
    00338   }
    00339
    00340   return NULL;
    00341 }
    00342
    00356 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
    00357 {
    00358   VehicleFromPos(tile, data, proc, false);
    00359 }
    00360
    00371 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
    00372 {
    00373   return VehicleFromPos(tile, data, proc, true) != NULL;
    00374 }
    00375
    00376
    00377 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
    00378 {
    00379   Vehicle **old_hash = v->old_new_hash;
    00380   Vehicle **new_hash;
    00381
    00382   if (remove) {
    00383     new_hash = NULL;
    00384   } else {
    00385     int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
    00386     int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
    00387     new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
    00388   }
    00389
    00390   if (old_hash == new_hash) return;
    00391
    00392   /* Remove from the old position in the hash table */
    00393   if (old_hash != NULL) {
    00394     Vehicle *last = NULL;
    00395     Vehicle *u = *old_hash;
    00396     while (u != v) {
    00397       last = u;
    00398       u = u->next_new_hash;
    00399       assert(u != NULL);
    00400     }
    00401
    00402     if (last == NULL) {
    00403       *old_hash = v->next_new_hash;
    00404     } else {
    00405       last->next_new_hash = v->next_new_hash;
    00406     }
    00407   }
    00408
    00409   /* Insert vehicle at beginning of the new position in the hash table */
    00410   if (new_hash != NULL) {
    00411     v->next_new_hash = *new_hash;
    00412     *new_hash = v;
    00413     assert(v != v->next_new_hash);
    00414   }
    00415
    00416   /* Remember current hash position */
    00417   v->old_new_hash = new_hash;
    00418 }
    00419
    00420 static Vehicle *_vehicle_position_hash[0x1000];
    00421
    00422 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
    00423 {
    00424   UpdateNewVehiclePosHash(v, x == INVALID_COORD);
    00425
    00426   Vehicle **old_hash, **new_hash;
    00427   int old_x = v->coord.left;
    00428   int old_y = v->coord.top;
    00429
    00430   new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
    00431   old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
    00432
    00433   if (old_hash == new_hash) return;
    00434
    00435   /* remove from hash table? */
    00436   if (old_hash != NULL) {
    00437     Vehicle *last = NULL;
    00438     Vehicle *u = *old_hash;
    00439     while (u != v) {
    00440       last = u;
    00441       u = u->next_hash;
    00442       assert(u != NULL);
    00443     }
    00444
    00445     if (last == NULL) {
    00446       *old_hash = v->next_hash;
    00447     } else {
    00448       last->next_hash = v->next_hash;
    00449     }
    00450   }
    00451
    00452   /* insert into hash table? */
    00453   if (new_hash != NULL) {
    00454     v->next_hash = *new_hash;
    00455     *new_hash = v;
    00456   }
    00457 }
    00458
    00459 void ResetVehiclePosHash()
    00460 {
    00461   Vehicle *v;
    00462   FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
    00463   memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
    00464   memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
    00465 }
    00466
    00467 void ResetVehicleColourMap()
    00468 {
    00469   Vehicle *v;
    00470   FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
    00471 }
    00472
    00477 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
    00478 static AutoreplaceMap _vehicles_to_autoreplace;
    00479
    00480 void InitializeVehicles()
    00481 {
    00482   _Vehicle_pool.CleanPool();
    00483   _Vehicle_pool.AddBlockToPool();
    00484
    00485   _CargoPayment_pool.CleanPool();
    00486   _CargoPayment_pool.AddBlockToPool();
    00487
    00488   _cargo_payment_savegame = false;
    00489
    00490   _vehicles_to_autoreplace.Reset();
    00491   ResetVehiclePosHash();
    00492 }
    00493
    00494 Vehicle *GetLastVehicleInChain(Vehicle *v)
    00495 {
    00496   while (v->Next() != NULL) v = v->Next();
    00497   return v;
    00498 }
    00499
    00500 const Vehicle *GetLastVehicleInChain(const Vehicle *v)
    00501 {
    00502   while (v->Next() != NULL) v = v->Next();
    00503   return v;
    00504 }
    00505
    00506 uint CountVehiclesInChain(const Vehicle *v)
    00507 {
    00508   uint count = 0;
    00509   do count++; while ((v = v->Next()) != NULL);
    00510   return count;
    00511 }
    00512
    00517 bool IsEngineCountable(const Vehicle *v)
    00518 {
    00519   switch (v->type) {
    00520     case VEH_AIRCRAFT: return IsNormalAircraft(v); // don't count plane shadows and helicopter rotors
    00521     case VEH_TRAIN:
    00522       return !IsArticulatedPart(v) && // tenders and other articulated parts
    00523       !IsRearDualheaded(v); // rear parts of multiheaded engines
    00524     case VEH_ROAD: return IsRoadVehFront(v);
    00525     case VEH_SHIP: return true;
    00526     default: return false; // Only count company buildable vehicles
    00527   }
    00528 }
    00529
    00530 void Vehicle::PreDestructor()
    00531 {
    00532   if (CleaningPool()) return;
    00533
    00534   if (IsValidStationID(this->last_station_visited)) {
    00535     GetStation(this->last_station_visited)->loading_vehicles.remove(this);
    00536
    00537     HideFillingPercent(&this->fill_percent_te_id);
    00538
    00539     delete this->cargo_payment;
    00540   }
    00541
    00542   if (IsEngineCountable(this)) {
    00543     GetCompany(this->owner)->num_engines[this->engine_type]--;
    00544     if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
    00545
    00546     DeleteGroupHighlightOfVehicle(this);
    00547     if (IsValidGroupID(this->group_id)) GetGroup(this->group_id)->num_engines[this->engine_type]--;
    00548     if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
    00549   }
    00550
    00551   if (this->type == VEH_ROAD) ClearSlot(this);
    00552   if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
    00553     Station *st = GetTargetAirportIfValid(this);
    00554     if (st != NULL) {
    00555       const AirportFTA *layout = st->Airport()->layout;
    00556       CLRBITS(st->airport_flags, layout[this->u.air.previous_pos].block | layout[this->u.air.pos].block);
    00557     }
    00558   }
    00559
    00560   if (this->type != VEH_TRAIN || (this->type == VEH_TRAIN && (IsFrontEngine(this) || IsFreeWagon(this)))) {
    00561     InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
    00562   }
    00563
    00564   if (this->IsPrimaryVehicle()) {
    00565     DeleteWindowById(WC_VEHICLE_VIEW, this->index);
    00566     DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
    00567     DeleteWindowById(WC_VEHICLE_REFIT, this->index);
    00568     DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
    00569     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
    00570     InvalidateWindow(WC_COMPANY, this->owner);
    00571   }
    00572   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
    00573
    00574   this->cargo.Truncate(0);
    00575   DeleteVehicleOrders(this);
    00576   DeleteDepotHighlightOfVehicle(this);
    00577
    00578   extern void StopGlobalFollowVehicle(const Vehicle *v);
    00579   StopGlobalFollowVehicle(this);
    00580
    00581   ReleaseDisastersTargetingVehicle(this->index);
    00582 }
    00583
    00584 Vehicle::~Vehicle()
    00585 {
    00586   free(this->name);
    00587
    00588   if (CleaningPool()) return;
    00589
    00590   /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
    00591    * it may happen that vehicle chain is deleted when visible */
    00592   if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
    00593
    00594   Vehicle *v = this->Next();
    00595   this->SetNext(NULL);
    00596
    00597   delete v;
    00598
    00599   UpdateVehiclePosHash(this, INVALID_COORD, 0);
    00600   this->next_hash = NULL;
    00601   this->next_new_hash = NULL;
    00602
    00603   DeleteVehicleNews(this->index, INVALID_STRING_ID);
    00604
    00605   this->type = VEH_INVALID;
    00606 }
    00607
    00611 void VehicleEnteredDepotThisTick(Vehicle *v)
    00612 {
    00613   /* Vehicle should stop in the depot if it was in 'stopping' state */
    00614   _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
    00615
    00616   /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
    00617    * stopping in the depot, so we stop it to ensure that it will not reserve
    00618    * the path out of the depot before we might autoreplace it to a different
    00619    * engine. The new engine would not own the reserved path we store that we
    00620    * stopped the vehicle, so autoreplace can start it again */
    00621   v->vehstatus |= VS_STOPPED;
    00622 }
    00623
    00624 void CallVehicleTicks()
    00625 {
    00626   _vehicles_to_autoreplace.Clear();
    00627
    00628   Station *st;
    00629   FOR_ALL_STATIONS(st) LoadUnloadStation(st);
    00630
    00631   Vehicle *v;
    00632   FOR_ALL_VEHICLES(v) {
    00633     v->Tick();
    00634
    00635     switch (v->type) {
    00636       default: break;
    00637
    00638       case VEH_TRAIN:
    00639       case VEH_ROAD:
    00640       case VEH_AIRCRAFT:
    00641       case VEH_SHIP:
    00642         if (v->type == VEH_TRAIN && IsTrainWagon(v)) continue;
    00643         if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
    00644         if (v->type == VEH_ROAD && !IsRoadVehFront(v)) continue;
    00645
    00646         v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
    00647         /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
    00648         if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
    00649
    00650         /* Play an alterate running sound every 16 ticks */
    00651         if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
    00652     }
    00653   }
    00654
    00655   for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
    00656     v = it->first;
    00657     /* Autoreplace needs the current company set as the vehicle owner */
    00658     _current_company = v->owner;
    00659
    00660     /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
    00661      * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
    00662      * they are already leaving the depot again before being replaced. */
    00663     if (it->second) v->vehstatus &= ~VS_STOPPED;
    00664
    00665     /* Store the position of the effect as the vehicle pointer will become invalid later */
    00666     int x = v->x_pos;
    00667     int y = v->y_pos;
    00668     int z = v->z_pos;
    00669
    00670     const Company *c = GetCompany(_current_company);
    00671     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->engine_renew_money));
    00672     CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
    00673     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->engine_renew_money));
    00674
    00675     if (!IsLocalCompany()) continue;
    00676
    00677     if (res.Succeeded()) {
    00678       ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
    00679       continue;
    00680     }
    00681
    00682     StringID error_message = res.GetErrorMessage();
    00683     if (error_message == STR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
    00684
    00685     if (error_message == STR_0003_NOT_ENOUGH_CASH_REQUIRES) error_message = STR_AUTOREPLACE_MONEY_LIMIT;
    00686
    00687     StringID message;
    00688     if (error_message == STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
    00689       message = error_message;
    00690     } else {
    00691       message = STR_VEHICLE_AUTORENEW_FAILED;
    00692     }
    00693
    00694     SetDParam(0, v->index);
    00695     SetDParam(1, error_message);
    00696     AddNewsItem(message, NS_ADVICE, v->index, 0);
    00697   }
    00698
    00699   _current_company = OWNER_NONE;
    00700 }
    00701
    00707 bool CanRefitTo(EngineID engine_type, CargoID cid_to)
    00708 {
    00709   return HasBit(EngInfo(engine_type)->refit_mask, cid_to);
    00710 }
    00711
    00716 CargoID FindFirstRefittableCargo(EngineID engine_type)
    00717 {
    00718   uint32 refit_mask = EngInfo(engine_type)->refit_mask;
    00719
    00720   if (refit_mask != 0) {
    00721     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
    00722       if (HasBit(refit_mask, cid)) return cid;
    00723     }
    00724   }
    00725
    00726   return CT_INVALID;
    00727 }
    00728
    00733 CommandCost GetRefitCost(EngineID engine_type)
    00734 {
    00735   Money base_cost;
    00736   ExpensesType expense_type;
    00737   switch (GetEngine(engine_type)->type) {
    00738     case VEH_SHIP:
    00739       base_cost = _price.ship_base;
    00740       expense_type = EXPENSES_SHIP_RUN;
    00741       break;
    00742
    00743     case VEH_ROAD:
    00744       base_cost = _price.roadveh_base;
    00745       expense_type = EXPENSES_ROADVEH_RUN;
    00746       break;
    00747
    00748     case VEH_AIRCRAFT:
    00749       base_cost = _price.aircraft_base;
    00750       expense_type = EXPENSES_AIRCRAFT_RUN;
    00751       break;
    00752
    00753     case VEH_TRAIN:
    00754       base_cost = 2 * ((RailVehInfo(engine_type)->railveh_type == RAILVEH_WAGON) ?
    00755                _price.build_railwagon : _price.build_railvehicle);
    00756       expense_type = EXPENSES_TRAIN_RUN;
    00757       break;
    00758
    00759     default: NOT_REACHED();
    00760   }
    00761   return CommandCost(expense_type, (EngInfo(engine_type)->refit_cost * base_cost) >> 10);
    00762 }
    00763
    00764 static void DoDrawVehicle(const Vehicle *v)
    00765 {
    00766   SpriteID image = v->cur_image;
    00767   SpriteID pal = PAL_NONE;
    00768
    00769   if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
    00770
    00771   AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
    00772     v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
    00773 }
    00774
    00775 void ViewportAddVehicles(DrawPixelInfo *dpi)
    00776 {
    00777   /* The bounding rectangle */
    00778   const int l = dpi->left;
    00779   const int r = dpi->left + dpi->width;
    00780   const int t = dpi->top;
    00781   const int b = dpi->top + dpi->height;
    00782
    00783   /* The hash area to scan */
    00784   int xl, xu, yl, yu;
    00785
    00786   if (dpi->width + 70 < (1 << (7 + 6))) {
    00787     xl = GB(l - 70, 7, 6);
    00788     xu = GB(r,      7, 6);
    00789   } else {
    00790     /* scan whole hash row */
    00791     xl = 0;
    00792     xu = 0x3F;
    00793   }
    00794
    00795   if (dpi->height + 70 < (1 << (6 + 6))) {
    00796     yl = GB(t - 70, 6, 6) << 6;
    00797     yu = GB(b,      6, 6) << 6;
    00798   } else {
    00799     /* scan whole column */
    00800     yl = 0;
    00801     yu = 0x3F << 6;
    00802   }
    00803
    00804   for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
    00805     for (int x = xl;; x = (x + 1) & 0x3F) {
    00806       const Vehicle *v = _vehicle_position_hash[x + y]; // already masked & 0xFFF
    00807
    00808       while (v != NULL) {
    00809         if (!(v->vehstatus & VS_HIDDEN) &&
    00810             l <= v->coord.right &&
    00811             t <= v->coord.bottom &&
    00812             r >= v->coord.left &&
    00813             b >= v->coord.top) {
    00814           DoDrawVehicle(v);
    00815         }
    00816         v = v->next_hash;
    00817       }
    00818
    00819       if (x == xu) break;
    00820     }
    00821
    00822     if (y == yu) break;
    00823   }
    00824 }
    00825
    00826 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
    00827 {
    00828   Vehicle *found = NULL, *v;
    00829   uint dist, best_dist = UINT_MAX;
    00830
    00831   if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
    00832
    00833   x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
    00834   y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
    00835
    00836   FOR_ALL_VEHICLES(v) {
    00837     if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
    00838         x >= v->coord.left && x <= v->coord.right &&
    00839         y >= v->coord.top && y <= v->coord.bottom) {
    00840
    00841       dist = max(
    00842         abs(((v->coord.left + v->coord.right) >> 1) - x),
    00843         abs(((v->coord.top + v->coord.bottom) >> 1) - y)
    00844       );
    00845
    00846       if (dist < best_dist) {
    00847         found = v;
    00848         best_dist = dist;
    00849       }
    00850     }
    00851   }
    00852
    00853   return found;
    00854 }
    00855
    00856 void CheckVehicle32Day(Vehicle *v)
    00857 {
    00858   if ((v->day_counter & 0x1F) != 0) return;
    00859
    00860   uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
    00861   if (callback == CALLBACK_FAILED) return;
    00862   if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
    00863   if (HasBit(callback, 1)) v->colourmap = PAL_NONE;                         // Update colourmap via callback 2D
    00864 }
    00865
    00866 void DecreaseVehicleValue(Vehicle *v)
    00867 {
    00868   v->value -= v->value >> 8;
    00869   InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    00870 }
    00871
    00872 static const byte _breakdown_chance[64] = {
    00873     3,   3,   3,   3,   3,   3,   3,   3,
    00874     4,   4,   5,   5,   6,   6,   7,   7,
    00875     8,   8,   9,   9,  10,  10,  11,  11,
    00876    12,  13,  13,  13,  13,  14,  15,  16,
    00877    17,  19,  21,  25,  28,  31,  34,  37,
    00878    40,  44,  48,  52,  56,  60,  64,  68,
    00879    72,  80,  90, 100, 110, 120, 130, 140,
    00880   150, 170, 190, 210, 230, 250, 250, 250,
    00881 };
    00882
    00883 void CheckVehicleBreakdown(Vehicle *v)
    00884 {
    00885   int rel, rel_old;
    00886
    00887   /* decrease reliability */
    00888   v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
    00889   if ((rel_old >> 8) != (rel >> 8)) InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    00890
    00891   if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED ||
    00892       _settings_game.difficulty.vehicle_breakdowns < 1 ||
    00893       v->cur_speed < 5 || _game_mode == GM_MENU) {
    00894     return;
    00895   }
    00896
    00897   uint32 r = Random();
    00898
    00899   /* increase chance of failure */
    00900   int chance = v->breakdown_chance + 1;
    00901   if (Chance16I(1, 25, r)) chance += 25;
    00902   v->breakdown_chance = min(255, chance);
    00903
    00904   /* calculate reliability value to use in comparison */
    00905   rel = v->reliability;
    00906   if (v->type == VEH_SHIP) rel += 0x6666;
    00907
    00908   /* reduced breakdowns? */
    00909   if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
    00910
    00911   /* check if to break down */
    00912   if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
    00913     v->breakdown_ctr    = GB(r, 16, 6) + 0x3F;
    00914     v->breakdown_delay  = GB(r, 24, 7) + 0x80;
    00915     v->breakdown_chance = 0;
    00916   }
    00917 }
    00918
    00919 void AgeVehicle(Vehicle *v)
    00920 {
    00921   if (v->age < 65535) v->age++;
    00922
    00923   int age = v->age - v->max_age;
    00924   if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
    00925       age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
    00926     v->reliability_spd_dec <<= 1;
    00927   }
    00928
    00929   InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    00930
    00931   /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
    00932   if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
    00933
    00934   /* Don't warn if a renew is active */
    00935   if (GetCompany(v->owner)->engine_renew && GetEngine(v->engine_type)->company_avail != 0) return;
    00936
    00937   StringID str;
    00938   if (age == -DAYS_IN_LEAP_YEAR) {
    00939     str = STR_01A0_IS_GETTING_OLD;
    00940   } else if (age == 0) {
    00941     str = STR_01A1_IS_GETTING_VERY_OLD;
    00942   } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
    00943     str = STR_01A2_IS_GETTING_VERY_OLD_AND;
    00944   } else {
    00945     return;
    00946   }
    00947
    00948   SetDParam(0, v->index);
    00949   AddNewsItem(str, NS_ADVICE, v->index, 0);
    00950 }
    00951
    00958 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
    00959 {
    00960   int count = 0;
    00961   int max = 0;
    00962   int cars = 0;
    00963   int unloading = 0;
    00964   bool loading = false;
    00965
    00966   const Vehicle *u = v;
    00967   const Station *st = v->last_station_visited != INVALID_STATION ? GetStation(v->last_station_visited) : NULL;
    00968
    00969   /* Count up max and used */
    00970   for (; v != NULL; v = v->Next()) {
    00971     count += v->cargo.Count();
    00972     max += v->cargo_cap;
    00973     if (v->cargo_cap != 0 && colour != NULL) {
    00974       unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
    00975       loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
    00976       cars++;
    00977     }
    00978   }
    00979
    00980   if (colour != NULL) {
    00981     if (unloading == 0 && loading) {
    00982       *colour = STR_PERCENT_UP;
    00983     } else if (cars == unloading || !loading) {
    00984       *colour = STR_PERCENT_DOWN;
    00985     } else {
    00986       *colour = STR_PERCENT_UP_DOWN;
    00987     }
    00988   }
    00989
    00990   /* Train without capacity */
    00991   if (max == 0) return 100;
    00992
    00993   /* Return the percentage */
    00994   return (count * 100) / max;
    00995 }
    00996
    00997 void VehicleEnterDepot(Vehicle *v)
    00998 {
    00999   switch (v->type) {
    01000     case VEH_TRAIN:
    01001       InvalidateWindowClasses(WC_TRAINS_LIST);
    01002       /* Clear path reservation */
    01003       SetDepotWaypointReservation(v->tile, false);
    01004       if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
    01005
    01006       if (!IsFrontEngine(v)) v = v->First();
    01007       UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
    01008       v->load_unload_time_rem = 0;
    01009       ClrBit(v->u.rail.flags, VRF_TOGGLE_REVERSE);
    01010       TrainConsistChanged(v, true);
    01011       break;
    01012
    01013     case VEH_ROAD:
    01014       InvalidateWindowClasses(WC_ROADVEH_LIST);
    01015       if (!IsRoadVehFront(v)) v = v->First();
    01016       break;
    01017
    01018     case VEH_SHIP:
    01019       InvalidateWindowClasses(WC_SHIPS_LIST);
    01020       v->u.ship.state = TRACK_BIT_DEPOT;
    01021       RecalcShipStuff(v);
    01022       break;
    01023
    01024     case VEH_AIRCRAFT:
    01025       InvalidateWindowClasses(WC_AIRCRAFT_LIST);
    01026       HandleAircraftEnterHangar(v);
    01027       break;
    01028     default: NOT_REACHED();
    01029   }
    01030
    01031   if (v->type != VEH_TRAIN) {
    01032     /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
    01033      * 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 */
    01034     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    01035   }
    01036   InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
    01037
    01038   v->vehstatus |= VS_HIDDEN;
    01039   v->cur_speed = 0;
    01040
    01041   VehicleServiceInDepot(v);
    01042
    01043   TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
    01044
    01045   if (v->current_order.IsType(OT_GOTO_DEPOT)) {
    01046     InvalidateWindow(WC_VEHICLE_VIEW, v->index);
    01047
    01048     const Order *real_order = GetVehicleOrder(v, v->cur_order_index);
    01049     Order t = v->current_order;
    01050     v->current_order.MakeDummy();
    01051
    01052     /* Test whether we are heading for this depot. If not, do nothing.
    01053      * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
    01054     if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
    01055         real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
    01056         (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
    01057       /* We are heading for another depot, keep driving. */
    01058       return;
    01059     }
    01060
    01061     if (t.IsRefit()) {
    01062       _current_company = v->owner;
    01063       CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
    01064
    01065       if (CmdFailed(cost)) {
    01066         _vehicles_to_autoreplace[v] = false;
    01067         if (v->owner == _local_company) {
    01068           /* Notify the user that we stopped the vehicle */
    01069           SetDParam(0, v->index);
    01070           AddNewsItem(STR_ORDER_REFIT_FAILED, NS_ADVICE, v->index, 0);
    01071         }
    01072       } else if (v->owner == _local_company && cost.GetCost() != 0) {
    01073         ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
    01074       }
    01075     }
    01076
    01077     if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
    01078       /* Part of orders */
    01079       UpdateVehicleTimetable(v, true);
    01080       v->cur_order_index++;
    01081     }
    01082     if (t.GetDepotActionType() & ODATFB_HALT) {
    01083       /* Vehicles are always stopped on entering depots. Do not restart this one. */
    01084       _vehicles_to_autoreplace[v] = false;
    01085       if (v->owner == _local_company) {
    01086         StringID string;
    01087
    01088         switch (v->type) {
    01089           case VEH_TRAIN:    string = STR_8814_TRAIN_IS_WAITING_IN_DEPOT; break;
    01090           case VEH_ROAD:     string = STR_9016_ROAD_VEHICLE_IS_WAITING;   break;
    01091           case VEH_SHIP:     string = STR_981C_SHIP_IS_WAITING_IN_DEPOT;  break;
    01092           case VEH_AIRCRAFT: string = STR_A014_AIRCRAFT_IS_WAITING_IN;    break;
    01093           default: NOT_REACHED(); string = STR_EMPTY; // Set the string to something to avoid a compiler warning
    01094         }
    01095
    01096         SetDParam(0, v->index);
    01097         AddNewsItem(string, NS_ADVICE, v->index, 0);
    01098       }
    01099       AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
    01100     }
    01101   }
    01102 }
    01103
    01104
    01112 void VehicleMove(Vehicle *v, bool update_viewport)
    01113 {
    01114   int img = v->cur_image;
    01115   Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
    01116   const Sprite *spr = GetSprite(img, ST_NORMAL);
    01117
    01118   pt.x += spr->x_offs;
    01119   pt.y += spr->y_offs;
    01120
    01121   UpdateVehiclePosHash(v, pt.x, pt.y);
    01122
    01123   Rect old_coord = v->coord;
    01124   v->coord.left   = pt.x;
    01125   v->coord.top    = pt.y;
    01126   v->coord.right  = pt.x + spr->width + 2;
    01127   v->coord.bottom = pt.y + spr->height + 2;
    01128
    01129   if (update_viewport) {
    01130     MarkAllViewportsDirty(
    01131       min(old_coord.left,   v->coord.left),
    01132       min(old_coord.top,    v->coord.top),
    01133       max(old_coord.right,  v->coord.right) + 1,
    01134       max(old_coord.bottom, v->coord.bottom) + 1
    01135     );
    01136   }
    01137 }
    01138
    01147 void MarkSingleVehicleDirty(const Vehicle *v)
    01148 {
    01149   MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
    01150 }
    01151
    01156 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
    01157 {
    01158   static const int8 _delta_coord[16] = {
    01159     -1,-1,-1, 0, 1, 1, 1, 0, /* x */
    01160     -1, 0, 1, 1, 1, 0,-1,-1, /* y */
    01161   };
    01162
    01163   int x = v->x_pos + _delta_coord[v->direction];
    01164   int y = v->y_pos + _delta_coord[v->direction + 8];
    01165
    01166   GetNewVehiclePosResult gp;
    01167   gp.x = x;
    01168   gp.y = y;
    01169   gp.old_tile = v->tile;
    01170   gp.new_tile = TileVirtXY(x, y);
    01171   return gp;
    01172 }
    01173
    01174 static const Direction _new_direction_table[] = {
    01175   DIR_N , DIR_NW, DIR_W ,
    01176   DIR_NE, DIR_SE, DIR_SW,
    01177   DIR_E , DIR_SE, DIR_S
    01178 };
    01179
    01180 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
    01181 {
    01182   int i = 0;
    01183
    01184   if (y >= v->y_pos) {
    01185     if (y != v->y_pos) i += 3;
    01186     i += 3;
    01187   }
    01188
    01189   if (x >= v->x_pos) {
    01190     if (x != v->x_pos) i++;
    01191     i++;
    01192   }
    01193
    01194   Direction dir = v->direction;
    01195
    01196   DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
    01197   if (dirdiff == DIRDIFF_SAME) return dir;
    01198   return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
    01199 }
    01200
    01201 Trackdir GetVehicleTrackdir(const Vehicle *v)
    01202 {
    01203   if (v->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
    01204
    01205   switch (v->type) {
    01206     case VEH_TRAIN:
    01207       if (v->u.rail.track == TRACK_BIT_DEPOT) // We'll assume the train is facing outwards
    01208         return DiagDirToDiagTrackdir(GetRailDepotDirection(v->tile)); // Train in depot
    01209
    01210       if (v->u.rail.track == TRACK_BIT_WORMHOLE) // train in tunnel or on bridge, so just use his direction and assume a diagonal track
    01211         return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
    01212
    01213       return TrackDirectionToTrackdir(FindFirstTrack(v->u.rail.track), v->direction);
    01214
    01215     case VEH_SHIP:
    01216       if (v->IsInDepot())
    01217         /* We'll assume the ship is facing outwards */
    01218         return DiagDirToDiagTrackdir(GetShipDepotDirection(v->tile));
    01219
    01220       if (v->u.ship.state == TRACK_BIT_WORMHOLE) // ship on aqueduct, so just use his direction and assume a diagonal track
    01221         return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
    01222
    01223       return TrackDirectionToTrackdir(FindFirstTrack(v->u.ship.state), v->direction);
    01224
    01225     case VEH_ROAD:
    01226       if (v->IsInDepot()) // We'll assume the road vehicle is facing outwards
    01227         return DiagDirToDiagTrackdir(GetRoadDepotDirection(v->tile));
    01228
    01229       if (IsStandardRoadStopTile(v->tile)) // We'll assume the road vehicle is facing outwards
    01230         return DiagDirToDiagTrackdir(GetRoadStopDir(v->tile)); // Road vehicle in a station
    01231
    01232       /* Drive through road stops / wormholes (tunnels) */
    01233       if (v->u.road.state > RVSB_TRACKDIR_MASK) return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
    01234
    01235       /* If vehicle's state is a valid track direction (vehicle is not turning around) return it,
    01236        * otherwise transform it into a valid track direction */
    01237       return (Trackdir)((IsReversingRoadTrackdir((Trackdir)v->u.road.state)) ? (v->u.road.state - 6) : v->u.road.state);
    01238
    01239     /* case VEH_AIRCRAFT: case VEH_EFFECT: case VEH_DISASTER: */
    01240     default: return INVALID_TRACKDIR;
    01241   }
    01242 }
    01243
    01253 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
    01254 {
    01255   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
    01256 }
    01257
    01258 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
    01259 {
    01260   /* Find maximum */
    01261   const Vehicle *v;
    01262   FOR_ALL_VEHICLES(v) {
    01263     if (v->type == type && v->owner == owner) {
    01264       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
    01265     }
    01266   }
    01267
    01268   if (this->maxid == 0) return;
    01269
    01270   this->maxid++; // so there is space for last item (with v->unitnumber == maxid)
    01271   this->maxid++; // this one will always be free (well, it will fail when there are 65535 units, so this overflows)
    01272
    01273   this->cache = CallocT<bool>(this->maxid);
    01274
    01275   /* Fill the cache */
    01276   FOR_ALL_VEHICLES(v) {
    01277     if (v->type == type && v->owner == owner) {
    01278       this->cache[v->unitnumber] = true;
    01279     }
    01280   }
    01281 }
    01282
    01283 UnitID FreeUnitIDGenerator::NextID()
    01284 {
    01285   if (this->maxid <= this->curid) return ++this->curid;
    01286
    01287   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
    01288
    01289   return this->curid;
    01290 }
    01291
    01292 UnitID GetFreeUnitNumber(VehicleType type)
    01293 {
    01294   FreeUnitIDGenerator gen(type, _current_company);
    01295
    01296   return gen.NextID();
    01297 }
    01298
    01299
    01308 bool CanBuildVehicleInfrastructure(VehicleType type)
    01309 {
    01310   assert(IsCompanyBuildableVehicleType(type));
    01311
    01312   if (!IsValidCompanyID(_local_company)) return false;
    01313   if (_settings_client.gui.always_build_infrastructure) return true;
    01314
    01315   UnitID max;
    01316   switch (type) {
    01317     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
    01318     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
    01319     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
    01320     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
    01321     default: NOT_REACHED();
    01322   }
    01323
    01324   /* We can build vehicle infrastructure when we may build the vehicle type */
    01325   if (max > 0) {
    01326     /* Can we actually build the vehicle type? */
    01327     const Engine *e;
    01328     FOR_ALL_ENGINES_OF_TYPE(e, type) {
    01329       if (HasBit(e->company_avail, _local_company)) return true;
    01330     }
    01331     return false;
    01332   }
    01333
    01334   /* We should be able to build infrastructure when we have the actual vehicle type */
    01335   const Vehicle *v;
    01336   FOR_ALL_VEHICLES(v) {
    01337     if (v->owner == _local_company && v->type == type) return true;
    01338   }
    01339
    01340   return false;
    01341 }
    01342
    01343
    01344 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
    01345 {
    01346   const Company *c = GetCompany(company);
    01347   LiveryScheme scheme = LS_DEFAULT;
    01348   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
    01349
    01350   /* The default livery is always available for use, but its in_use flag determines
    01351    * whether any _other_ liveries are in use. */
    01352   if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
    01353     /* Determine the livery scheme to use */
    01354     const Engine *e = GetEngine(engine_type);
    01355     switch (e->type) {
    01356       default: NOT_REACHED();
    01357       case VEH_TRAIN: {
    01358         const RailVehicleInfo *rvi = RailVehInfo(engine_type);
    01359         if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (IsArticulatedPart(v) && rvi->railveh_type != RAILVEH_WAGON))) {
    01360           /* Wagonoverrides use the coloir scheme of the front engine.
    01361            * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
    01362           engine_type = parent_engine_type;
    01363           e = GetEngine(engine_type);
    01364           rvi = RailVehInfo(engine_type);
    01365           /* Note: Luckily cargo_type is not needed for engines */
    01366         }
    01367
    01368         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01369         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01370         if (rvi->railveh_type == RAILVEH_WAGON) {
    01371           if (!GetCargo(cargo_type)->is_freight) {
    01372             if (parent_engine_type == INVALID_ENGINE) {
    01373               scheme = LS_PASSENGER_WAGON_STEAM;
    01374             } else {
    01375               switch (RailVehInfo(parent_engine_type)->engclass) {
    01376                 default: NOT_REACHED();
    01377                 case EC_STEAM:    scheme = LS_PASSENGER_WAGON_STEAM;    break;
    01378                 case EC_DIESEL:   scheme = LS_PASSENGER_WAGON_DIESEL;   break;
    01379                 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
    01380                 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
    01381                 case EC_MAGLEV:   scheme = LS_PASSENGER_WAGON_MAGLEV;   break;
    01382               }
    01383             }
    01384           } else {
    01385             scheme = LS_FREIGHT_WAGON;
    01386           }
    01387         } else {
    01388           bool is_mu = HasBit(EngInfo(engine_type)->misc_flags, EF_RAIL_IS_MU);
    01389
    01390           switch (rvi->engclass) {
    01391             default: NOT_REACHED();
    01392             case EC_STEAM:    scheme = LS_STEAM; break;
    01393             case EC_DIESEL:   scheme = is_mu ? LS_DMU : LS_DIESEL;   break;
    01394             case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
    01395             case EC_MONORAIL: scheme = LS_MONORAIL; break;
    01396             case EC_MAGLEV:   scheme = LS_MAGLEV; break;
    01397           }
    01398         }
    01399         break;
    01400       }
    01401
    01402       case VEH_ROAD: {
    01403         /* Always use the livery of the front */
    01404         if (v != NULL && parent_engine_type != INVALID_ENGINE) {
    01405           engine_type = parent_engine_type;
    01406           e = GetEngine(engine_type);
    01407           cargo_type = v->First()->cargo_type;
    01408         }
    01409         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01410         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01411
    01412         /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
    01413         if (HasBit(EngInfo(engine_type)->misc_flags, EF_ROAD_TRAM)) {
    01414           /* Tram */
    01415           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
    01416         } else {
    01417           /* Bus or truck */
    01418           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
    01419         }
    01420         break;
    01421       }
    01422
    01423       case VEH_SHIP: {
    01424         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01425         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01426         scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
    01427         break;
    01428       }
    01429
    01430       case VEH_AIRCRAFT: {
    01431         switch (e->u.air.subtype) {
    01432           case AIR_HELI: scheme = LS_HELICOPTER; break;
    01433           case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
    01434           case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
    01435         }
    01436         break;
    01437       }
    01438     }
    01439
    01440     /* Switch back to the default scheme if the resolved scheme is not in use */
    01441     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
    01442   }
    01443
    01444   return &c->livery[scheme];
    01445 }
    01446
    01447
    01448 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
    01449 {
    01450   SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
    01451
    01452   /* Return cached value if any */
    01453   if (map != PAL_NONE) return map;
    01454
    01455   /* Check if we should use the colour map callback */
    01456   if (HasBit(EngInfo(engine_type)->callbackmask, CBM_VEHICLE_COLOUR_REMAP)) {
    01457     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
    01458     /* A return value of 0xC000 is stated to "use the default two-colour
    01459      * maps" which happens to be the failure action too... */
    01460     if (callback != CALLBACK_FAILED && callback != 0xC000) {
    01461       map = GB(callback, 0, 14);
    01462       /* If bit 14 is set, then the company colours are applied to the
    01463        * map else it's returned as-is. */
    01464       if (!HasBit(callback, 14)) {
    01465         /* Update cache */
    01466         if (v != NULL) ((Vehicle*)v)->colourmap = map;
    01467         return map;
    01468       }
    01469     }
    01470   }
    01471
    01472   bool twocc = HasBit(EngInfo(engine_type)->misc_flags, EF_USES_2CC);
    01473
    01474   if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
    01475
    01476   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
    01477
    01478   map += livery->colour1;
    01479   if (twocc) map += livery->colour2 * 16;
    01480
    01481   /* Update cache */
    01482   if (v != NULL) ((Vehicle*)v)->colourmap = map;
    01483   return map;
    01484 }
    01485
    01486 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
    01487 {
    01488   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
    01489 }
    01490
    01491 SpriteID GetVehiclePalette(const Vehicle *v)
    01492 {
    01493   if (v->type == VEH_TRAIN) {
    01494     return GetEngineColourMap(v->engine_type, v->owner, v->u.rail.first_engine, v);
    01495   } else if (v->type == VEH_ROAD) {
    01496     return GetEngineColourMap(v->engine_type, v->owner, v->u.road.first_engine, v);
    01497   }
    01498
    01499   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
    01500 }
    01501
    01502
    01503 void Vehicle::BeginLoading()
    01504 {
    01505   assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
    01506
    01507   if (this->current_order.IsType(OT_GOTO_STATION) &&
    01508       this->current_order.GetDestination() == this->last_station_visited) {
    01509     current_order.MakeLoading(true);
    01510     UpdateVehicleTimetable(this, true);
    01511
    01512     /* Furthermore add the Non Stop flag to mark that this station
    01513      * is the actual destination of the vehicle, which is (for example)
    01514      * necessary to be known for HandleTrainLoading to determine
    01515      * whether the train is lost or not; not marking a train lost
    01516      * that arrives at random stations is bad. */
    01517     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
    01518
    01519   } else {
    01520     current_order.MakeLoading(false);
    01521   }
    01522
    01523   GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
    01524
    01525   PrepareUnload(this);
    01526
    01527   InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner);
    01528   InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01529   InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
    01530   InvalidateWindow(WC_STATION_VIEW, this->last_station_visited);
    01531
    01532   GetStation(this->last_station_visited)->MarkTilesDirty(true);
    01533   this->cur_speed = 0;
    01534   this->MarkDirty();
    01535 }
    01536
    01537 void Vehicle::LeaveStation()
    01538 {
    01539   assert(current_order.IsType(OT_LOADING));
    01540
    01541   delete this->cargo_payment;
    01542
    01543   /* Only update the timetable if the vehicle was supposed to stop here. */
    01544   if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
    01545
    01546   current_order.MakeLeaveStation();
    01547   Station *st = GetStation(this->last_station_visited);
    01548   st->loading_vehicles.remove(this);
    01549
    01550   HideFillingPercent(&this->fill_percent_te_id);
    01551
    01552   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
    01553     /* Trigger station animation (trains only) */
    01554     if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
    01555
    01556     /* Try to reserve a path when leaving the station as we
    01557      * might not be marked as wanting a reservation, e.g.
    01558      * when an overlength train gets turned around in a station. */
    01559     if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(GetVehicleTrackdir(this)), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
    01560       TryPathReserve(this, true, true);
    01561     }
    01562   }
    01563 }
    01564
    01565
    01566 void Vehicle::HandleLoading(bool mode)
    01567 {
    01568   switch (this->current_order.GetType()) {
    01569     case OT_LOADING: {
    01570       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
    01571
    01572       /* Not the first call for this tick, or still loading */
    01573       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
    01574           (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
    01575
    01576       this->PlayLeaveStationSound();
    01577
    01578       bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
    01579       this->LeaveStation();
    01580
    01581       /* If this was not the final order, don't remove it from the list. */
    01582       if (!at_destination_station) return;
    01583       break;
    01584     }
    01585
    01586     case OT_DUMMY: break;
    01587
    01588     default: return;
    01589   }
    01590
    01591   this->cur_order_index++;
    01592   InvalidateVehicleOrder(this, 0);
    01593 }
    01594
    01595 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
    01596 {
    01597   if (!CheckOwnership(this->owner)) return CMD_ERROR;
    01598   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
    01599   if (this->IsStoppedInDepot()) return CMD_ERROR;
    01600
    01601   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
    01602     bool halt_in_depot = this->current_order.GetDepotActionType() & ODATFB_HALT;
    01603     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
    01604       /* We called with a different DEPOT_SERVICE setting.
    01605        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
    01606        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
    01607       if (flags & DC_EXEC) {
    01608         this->current_order.SetDepotOrderType(ODTF_MANUAL);
    01609         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
    01610         InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01611       }
    01612       return CommandCost();
    01613     }
    01614
    01615     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
    01616     if (flags & DC_EXEC) {
    01617       /* If the orders to 'goto depot' are in the orders list (forced servicing),
    01618        * then skip to the next order; effectively cancelling this forced service */
    01619       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->cur_order_index++;
    01620
    01621       this->current_order.MakeDummy();
    01622       InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01623     }
    01624     return CommandCost();
    01625   }
    01626
    01627   TileIndex location;
    01628   DestinationID destination;
    01629   bool reverse;
    01630   static const StringID no_depot[] = {STR_883A_UNABLE_TO_FIND_ROUTE_TO, STR_9019_UNABLE_TO_FIND_LOCAL_DEPOT, STR_981A_UNABLE_TO_FIND_LOCAL_DEPOT, STR_A012_CAN_T_SEND_AIRCRAFT_TO};
    01631   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
    01632
    01633   if (flags & DC_EXEC) {
    01634     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
    01635
    01636     this->dest_tile = location;
    01637     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
    01638     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
    01639     InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01640
    01641     /* If there is no depot in front, reverse automatically (trains only) */
    01642     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
    01643
    01644     if (this->type == VEH_AIRCRAFT && this->u.air.state == FLYING && this->u.air.targetairport != destination) {
    01645       /* The aircraft is now heading for a different hangar than the next in the orders */
    01646       extern void AircraftNextAirportPos_and_Order(Vehicle *v);
    01647       AircraftNextAirportPos_and_Order(this);
    01648     }
    01649   }
    01650
    01651   return CommandCost();
    01652
    01653 }
    01654
    01655 void Vehicle::SetNext(Vehicle *next)
    01656 {
    01657   if (this->next != NULL) {
    01658     /* We had an old next vehicle. Update the first and previous pointers */
    01659     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    01660       v->first = this->next;
    01661     }
    01662     this->next->previous = NULL;
    01663   }
    01664
    01665   this->next = next;
    01666
    01667   if (this->next != NULL) {
    01668     /* A new next vehicle. Update the first and previous pointers */
    01669     if (this->next->previous != NULL) this->next->previous->next = NULL;
    01670     this->next->previous = this;
    01671     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    01672       v->first = this->first;
    01673     }
    01674   }
    01675 }
    01676
    01677 void Vehicle::AddToShared(Vehicle *shared_chain)
    01678 {
    01679   assert(this->previous_shared == NULL && this->next_shared == NULL);
    01680
    01681   if (!shared_chain->orders.list) {
    01682     assert(shared_chain->previous_shared == NULL);
    01683     assert(shared_chain->next_shared == NULL);
    01684     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
    01685   }
    01686
    01687   this->next_shared     = shared_chain->next_shared;
    01688   this->previous_shared = shared_chain;
    01689
    01690   shared_chain->next_shared = this;
    01691
    01692   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
    01693
    01694   shared_chain->orders.list->AddVehicle(this);
    01695 }
    01696
    01697 void Vehicle::RemoveFromShared()
    01698 {
    01699   /* Remember if we were first and the old window number before RemoveVehicle()
    01700    * as this changes first if needed. */
    01701   bool were_first = (this->FirstShared() == this);
    01702   uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
    01703
    01704   this->orders.list->RemoveVehicle(this);
    01705
    01706   if (!were_first) {
    01707     /* We are not the first shared one, so only relink our previous one. */
    01708     this->previous_shared->next_shared = this->NextShared();
    01709   }
    01710
    01711   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
    01712
    01713
    01714   if (this->orders.list->GetNumVehicles() == 1) {
    01715     /* When there is only one vehicle, remove the shared order list window. */
    01716     DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
    01717     InvalidateVehicleOrder(this->FirstShared(), 0);
    01718   } else if (were_first) {
    01719     /* If we were the first one, update to the new first one.
    01720      * Note: FirstShared() is already the new first */
    01721     InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
    01722   }
    01723
    01724   this->next_shared     = NULL;
    01725   this->previous_shared = NULL;
    01726 }
    01727
    01728 void StopAllVehicles()
    01729 {
    01730   Vehicle *v;
    01731   FOR_ALL_VEHICLES(v) {
    01732     /* Code ripped from CmdStartStopTrain. Can't call it, because of
    01733      * ownership problems, so we'll duplicate some code, for now */
    01734     v->vehstatus |= VS_STOPPED;
    01735     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    01736     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
    01737     v->MarkDirty();
    01738   }
    01739 }
    01740
    01741 void VehiclesYearlyLoop()
    01742 {
    01743   Vehicle *v;
    01744   FOR_ALL_VEHICLES(v) {
    01745     if (v->IsPrimaryVehicle()) {
    01746       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
    01747       Money profit = v->GetDisplayProfitThisYear();
    01748       if (v->age >= 730 && profit < 0) {
    01749         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
    01750           SetDParam(0, v->index);
    01751           SetDParam(1, profit);
    01752           AddNewsItem(
    01753             STR_VEHICLE_IS_UNPROFITABLE,
    01754             NS_ADVICE,
    01755             v->index,
    01756             0);
    01757         }
    01758         AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
    01759       }
    01760
    01761       v->profit_last_year = v->profit_this_year;
    01762       v->profit_this_year = 0;
    01763       InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    01764     }
    01765   }
    01766 }
    01767
    01768
    01778 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
    01779 {
    01780   assert(IsEngineIndex(engine_type));
    01781   const Engine *e = GetEngine(engine_type);
    01782
    01783   switch (e->type) {
    01784     case VEH_TRAIN:
    01785       return (st->facilities & FACIL_TRAIN) != 0;
    01786
    01787     case VEH_ROAD:
    01788       /* For road vehicles we need the vehicle to know whether it can actually
    01789        * use the station, but if it doesn't have facilities for RVs it is
    01790        * certainly not possible that the station can be used. */
    01791       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
    01792
    01793     case VEH_SHIP:
    01794       return (st->facilities & FACIL_DOCK) != 0;
    01795
    01796     case VEH_AIRCRAFT:
    01797       return (st->facilities & FACIL_AIRPORT) != 0 &&
    01798           (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
    01799
    01800     default:
    01801       return false;
    01802   }
    01803 }
    01804
    01811 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
    01812 {
    01813   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(v) != NULL;
    01814
    01815   return CanVehicleUseStation(v->engine_type, st);
    01816 }