# vehicle.cpp

Go to the documentation of this file.

    00001 /* $Id: vehicle.cpp 16742 2009-07-04 17:39:00Z 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
    00540   if (IsEngineCountable(this)) {
    00541     GetCompany(this->owner)->num_engines[this->engine_type]--;
    00542     if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
    00543
    00544     DeleteGroupHighlightOfVehicle(this);
    00545     if (IsValidGroupID(this->group_id)) GetGroup(this->group_id)->num_engines[this->engine_type]--;
    00546     if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
    00547   }
    00548
    00549   if (this->type == VEH_ROAD) ClearSlot(this);
    00550   if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
    00551     Station *st = GetTargetAirportIfValid(this);
    00552     if (st != NULL) {
    00553       const AirportFTA *layout = st->Airport()->layout;
    00554       CLRBITS(st->airport_flags, layout[this->u.air.previous_pos].block | layout[this->u.air.pos].block);
    00555     }
    00556   }
    00557
    00558   if (this->type != VEH_TRAIN || (this->type == VEH_TRAIN && (IsFrontEngine(this) || IsFreeWagon(this)))) {
    00559     InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
    00560   }
    00561
    00562   if (this->IsPrimaryVehicle()) {
    00563     DeleteWindowById(WC_VEHICLE_VIEW, this->index);
    00564     DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
    00565     DeleteWindowById(WC_VEHICLE_REFIT, this->index);
    00566     DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
    00567     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
    00568     InvalidateWindow(WC_COMPANY, this->owner);
    00569   }
    00570   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
    00571
    00572   this->cargo.Truncate(0);
    00573   DeleteVehicleOrders(this);
    00574   DeleteDepotHighlightOfVehicle(this);
    00575
    00576   extern void StopGlobalFollowVehicle(const Vehicle *v);
    00577   StopGlobalFollowVehicle(this);
    00578
    00579   ReleaseDisastersTargetingVehicle(this->index);
    00580 }
    00581
    00582 Vehicle::~Vehicle()
    00583 {
    00584   free(this->name);
    00585
    00586   if (CleaningPool()) return;
    00587
    00588   /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
    00589    * it may happen that vehicle chain is deleted when visible */
    00590   if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
    00591
    00592   Vehicle *v = this->Next();
    00593   this->SetNext(NULL);
    00594
    00595   delete v;
    00596
    00597   UpdateVehiclePosHash(this, INVALID_COORD, 0);
    00598   this->next_hash = NULL;
    00599   this->next_new_hash = NULL;
    00600
    00601   DeleteVehicleNews(this->index, INVALID_STRING_ID);
    00602
    00603   this->type = VEH_INVALID;
    00604 }
    00605
    00609 void VehicleEnteredDepotThisTick(Vehicle *v)
    00610 {
    00611   /* Vehicle should stop in the depot if it was in 'stopping' state */
    00612   _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
    00613
    00614   /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
    00615    * stopping in the depot, so we stop it to ensure that it will not reserve
    00616    * the path out of the depot before we might autoreplace it to a different
    00617    * engine. The new engine would not own the reserved path we store that we
    00618    * stopped the vehicle, so autoreplace can start it again */
    00619   v->vehstatus |= VS_STOPPED;
    00620 }
    00621
    00622 void CallVehicleTicks()
    00623 {
    00624   _vehicles_to_autoreplace.Clear();
    00625
    00626   Station *st;
    00627   FOR_ALL_STATIONS(st) LoadUnloadStation(st);
    00628
    00629   Vehicle *v;
    00630   FOR_ALL_VEHICLES(v) {
    00631     v->Tick();
    00632
    00633     switch (v->type) {
    00634       default: break;
    00635
    00636       case VEH_TRAIN:
    00637       case VEH_ROAD:
    00638       case VEH_AIRCRAFT:
    00639       case VEH_SHIP:
    00640         if (v->type == VEH_TRAIN && IsTrainWagon(v)) continue;
    00641         if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
    00642         if (v->type == VEH_ROAD && !IsRoadVehFront(v)) continue;
    00643
    00644         v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
    00645         /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
    00646         if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
    00647
    00648         /* Play an alterate running sound every 16 ticks */
    00649         if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
    00650     }
    00651   }
    00652
    00653   for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
    00654     v = it->first;
    00655     /* Autoreplace needs the current company set as the vehicle owner */
    00656     _current_company = v->owner;
    00657
    00658     /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
    00659      * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
    00660      * they are already leaving the depot again before being replaced. */
    00661     if (it->second) v->vehstatus &= ~VS_STOPPED;
    00662
    00663     /* Store the position of the effect as the vehicle pointer will become invalid later */
    00664     int x = v->x_pos;
    00665     int y = v->y_pos;
    00666     int z = v->z_pos;
    00667
    00668     const Company *c = GetCompany(_current_company);
    00669     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->engine_renew_money));
    00670     CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
    00671     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->engine_renew_money));
    00672
    00673     if (!IsLocalCompany()) continue;
    00674
    00675     if (res.Succeeded()) {
    00676       ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
    00677       continue;
    00678     }
    00679
    00680     StringID error_message = res.GetErrorMessage();
    00681     if (error_message == STR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
    00682
    00683     if (error_message == STR_0003_NOT_ENOUGH_CASH_REQUIRES) error_message = STR_AUTOREPLACE_MONEY_LIMIT;
    00684
    00685     StringID message;
    00686     if (error_message == STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
    00687       message = error_message;
    00688     } else {
    00689       message = STR_VEHICLE_AUTORENEW_FAILED;
    00690     }
    00691
    00692     SetDParam(0, v->index);
    00693     SetDParam(1, error_message);
    00694     AddNewsItem(message, NS_ADVICE, v->index, 0);
    00695   }
    00696
    00697   _current_company = OWNER_NONE;
    00698 }
    00699
    00705 bool CanRefitTo(EngineID engine_type, CargoID cid_to)
    00706 {
    00707   return HasBit(EngInfo(engine_type)->refit_mask, cid_to);
    00708 }
    00709
    00714 CargoID FindFirstRefittableCargo(EngineID engine_type)
    00715 {
    00716   uint32 refit_mask = EngInfo(engine_type)->refit_mask;
    00717
    00718   if (refit_mask != 0) {
    00719     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
    00720       if (HasBit(refit_mask, cid)) return cid;
    00721     }
    00722   }
    00723
    00724   return CT_INVALID;
    00725 }
    00726
    00731 CommandCost GetRefitCost(EngineID engine_type)
    00732 {
    00733   Money base_cost;
    00734   ExpensesType expense_type;
    00735   switch (GetEngine(engine_type)->type) {
    00736     case VEH_SHIP:
    00737       base_cost = _price.ship_base;
    00738       expense_type = EXPENSES_SHIP_RUN;
    00739       break;
    00740
    00741     case VEH_ROAD:
    00742       base_cost = _price.roadveh_base;
    00743       expense_type = EXPENSES_ROADVEH_RUN;
    00744       break;
    00745
    00746     case VEH_AIRCRAFT:
    00747       base_cost = _price.aircraft_base;
    00748       expense_type = EXPENSES_AIRCRAFT_RUN;
    00749       break;
    00750
    00751     case VEH_TRAIN:
    00752       base_cost = 2 * ((RailVehInfo(engine_type)->railveh_type == RAILVEH_WAGON) ?
    00753                _price.build_railwagon : _price.build_railvehicle);
    00754       expense_type = EXPENSES_TRAIN_RUN;
    00755       break;
    00756
    00757     default: NOT_REACHED();
    00758   }
    00759   return CommandCost(expense_type, (EngInfo(engine_type)->refit_cost * base_cost) >> 10);
    00760 }
    00761
    00762 static void DoDrawVehicle(const Vehicle *v)
    00763 {
    00764   SpriteID image = v->cur_image;
    00765   SpriteID pal = PAL_NONE;
    00766
    00767   if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
    00768
    00769   AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
    00770     v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
    00771 }
    00772
    00773 void ViewportAddVehicles(DrawPixelInfo *dpi)
    00774 {
    00775   /* The bounding rectangle */
    00776   const int l = dpi->left;
    00777   const int r = dpi->left + dpi->width;
    00778   const int t = dpi->top;
    00779   const int b = dpi->top + dpi->height;
    00780
    00781   /* The hash area to scan */
    00782   int xl, xu, yl, yu;
    00783
    00784   if (dpi->width + 70 < (1 << (7 + 6))) {
    00785     xl = GB(l - 70, 7, 6);
    00786     xu = GB(r,      7, 6);
    00787   } else {
    00788     /* scan whole hash row */
    00789     xl = 0;
    00790     xu = 0x3F;
    00791   }
    00792
    00793   if (dpi->height + 70 < (1 << (6 + 6))) {
    00794     yl = GB(t - 70, 6, 6) << 6;
    00795     yu = GB(b,      6, 6) << 6;
    00796   } else {
    00797     /* scan whole column */
    00798     yl = 0;
    00799     yu = 0x3F << 6;
    00800   }
    00801
    00802   for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
    00803     for (int x = xl;; x = (x + 1) & 0x3F) {
    00804       const Vehicle *v = _vehicle_position_hash[x + y]; // already masked & 0xFFF
    00805
    00806       while (v != NULL) {
    00807         if (!(v->vehstatus & VS_HIDDEN) &&
    00808             l <= v->coord.right &&
    00809             t <= v->coord.bottom &&
    00810             r >= v->coord.left &&
    00811             b >= v->coord.top) {
    00812           DoDrawVehicle(v);
    00813         }
    00814         v = v->next_hash;
    00815       }
    00816
    00817       if (x == xu) break;
    00818     }
    00819
    00820     if (y == yu) break;
    00821   }
    00822 }
    00823
    00824 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
    00825 {
    00826   Vehicle *found = NULL, *v;
    00827   uint dist, best_dist = UINT_MAX;
    00828
    00829   if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
    00830
    00831   x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
    00832   y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
    00833
    00834   FOR_ALL_VEHICLES(v) {
    00835     if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
    00836         x >= v->coord.left && x <= v->coord.right &&
    00837         y >= v->coord.top && y <= v->coord.bottom) {
    00838
    00839       dist = max(
    00840         abs(((v->coord.left + v->coord.right) >> 1) - x),
    00841         abs(((v->coord.top + v->coord.bottom) >> 1) - y)
    00842       );
    00843
    00844       if (dist < best_dist) {
    00845         found = v;
    00846         best_dist = dist;
    00847       }
    00848     }
    00849   }
    00850
    00851   return found;
    00852 }
    00853
    00854 void CheckVehicle32Day(Vehicle *v)
    00855 {
    00856   if ((v->day_counter & 0x1F) != 0) return;
    00857
    00858   uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
    00859   if (callback == CALLBACK_FAILED) return;
    00860   if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
    00861   if (HasBit(callback, 1)) v->colourmap = PAL_NONE;                         // Update colourmap via callback 2D
    00862 }
    00863
    00864 void DecreaseVehicleValue(Vehicle *v)
    00865 {
    00866   v->value -= v->value >> 8;
    00867   InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    00868 }
    00869
    00870 static const byte _breakdown_chance[64] = {
    00871     3,   3,   3,   3,   3,   3,   3,   3,
    00872     4,   4,   5,   5,   6,   6,   7,   7,
    00873     8,   8,   9,   9,  10,  10,  11,  11,
    00874    12,  13,  13,  13,  13,  14,  15,  16,
    00875    17,  19,  21,  25,  28,  31,  34,  37,
    00876    40,  44,  48,  52,  56,  60,  64,  68,
    00877    72,  80,  90, 100, 110, 120, 130, 140,
    00878   150, 170, 190, 210, 230, 250, 250, 250,
    00879 };
    00880
    00881 void CheckVehicleBreakdown(Vehicle *v)
    00882 {
    00883   int rel, rel_old;
    00884
    00885   /* decrease reliability */
    00886   v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
    00887   if ((rel_old >> 8) != (rel >> 8)) InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    00888
    00889   if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED ||
    00890       _settings_game.difficulty.vehicle_breakdowns < 1 ||
    00891       v->cur_speed < 5 || _game_mode == GM_MENU) {
    00892     return;
    00893   }
    00894
    00895   uint32 r = Random();
    00896
    00897   /* increase chance of failure */
    00898   int chance = v->breakdown_chance + 1;
    00899   if (Chance16I(1, 25, r)) chance += 25;
    00900   v->breakdown_chance = min(255, chance);
    00901
    00902   /* calculate reliability value to use in comparison */
    00903   rel = v->reliability;
    00904   if (v->type == VEH_SHIP) rel += 0x6666;
    00905
    00906   /* reduced breakdowns? */
    00907   if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
    00908
    00909   /* check if to break down */
    00910   if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
    00911     v->breakdown_ctr    = GB(r, 16, 6) + 0x3F;
    00912     v->breakdown_delay  = GB(r, 24, 7) + 0x80;
    00913     v->breakdown_chance = 0;
    00914   }
    00915 }
    00916
    00917 void AgeVehicle(Vehicle *v)
    00918 {
    00919   if (v->age < 65535) v->age++;
    00920
    00921   int age = v->age - v->max_age;
    00922   if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
    00923       age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
    00924     v->reliability_spd_dec <<= 1;
    00925   }
    00926
    00927   InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    00928
    00929   /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
    00930   if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
    00931
    00932   /* Don't warn if a renew is active */
    00933   if (GetCompany(v->owner)->engine_renew && GetEngine(v->engine_type)->company_avail != 0) return;
    00934
    00935   StringID str;
    00936   if (age == -DAYS_IN_LEAP_YEAR) {
    00937     str = STR_01A0_IS_GETTING_OLD;
    00938   } else if (age == 0) {
    00939     str = STR_01A1_IS_GETTING_VERY_OLD;
    00940   } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
    00941     str = STR_01A2_IS_GETTING_VERY_OLD_AND;
    00942   } else {
    00943     return;
    00944   }
    00945
    00946   SetDParam(0, v->index);
    00947   AddNewsItem(str, NS_ADVICE, v->index, 0);
    00948 }
    00949
    00956 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
    00957 {
    00958   int count = 0;
    00959   int max = 0;
    00960   int cars = 0;
    00961   int unloading = 0;
    00962   bool loading = false;
    00963
    00964   const Vehicle *u = v;
    00965   const Station *st = v->last_station_visited != INVALID_STATION ? GetStation(v->last_station_visited) : NULL;
    00966
    00967   /* Count up max and used */
    00968   for (; v != NULL; v = v->Next()) {
    00969     count += v->cargo.Count();
    00970     max += v->cargo_cap;
    00971     if (v->cargo_cap != 0 && colour != NULL) {
    00972       unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
    00973       loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
    00974       cars++;
    00975     }
    00976   }
    00977
    00978   if (colour != NULL) {
    00979     if (unloading == 0 && loading) {
    00980       *colour = STR_PERCENT_UP;
    00981     } else if (cars == unloading || !loading) {
    00982       *colour = STR_PERCENT_DOWN;
    00983     } else {
    00984       *colour = STR_PERCENT_UP_DOWN;
    00985     }
    00986   }
    00987
    00988   /* Train without capacity */
    00989   if (max == 0) return 100;
    00990
    00991   /* Return the percentage */
    00992   return (count * 100) / max;
    00993 }
    00994
    00995 void VehicleEnterDepot(Vehicle *v)
    00996 {
    00997   switch (v->type) {
    00998     case VEH_TRAIN:
    00999       InvalidateWindowClasses(WC_TRAINS_LIST);
    01000       /* Clear path reservation */
    01001       SetDepotWaypointReservation(v->tile, false);
    01002       if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
    01003
    01004       if (!IsFrontEngine(v)) v = v->First();
    01005       UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
    01006       v->load_unload_time_rem = 0;
    01007       ClrBit(v->u.rail.flags, VRF_TOGGLE_REVERSE);
    01008       TrainConsistChanged(v, true);
    01009       break;
    01010
    01011     case VEH_ROAD:
    01012       InvalidateWindowClasses(WC_ROADVEH_LIST);
    01013       if (!IsRoadVehFront(v)) v = v->First();
    01014       break;
    01015
    01016     case VEH_SHIP:
    01017       InvalidateWindowClasses(WC_SHIPS_LIST);
    01018       v->u.ship.state = TRACK_BIT_DEPOT;
    01019       RecalcShipStuff(v);
    01020       break;
    01021
    01022     case VEH_AIRCRAFT:
    01023       InvalidateWindowClasses(WC_AIRCRAFT_LIST);
    01024       HandleAircraftEnterHangar(v);
    01025       break;
    01026     default: NOT_REACHED();
    01027   }
    01028
    01029   if (v->type != VEH_TRAIN) {
    01030     /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
    01031      * 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 */
    01032     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    01033   }
    01034   InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
    01035
    01036   v->vehstatus |= VS_HIDDEN;
    01037   v->cur_speed = 0;
    01038
    01039   VehicleServiceInDepot(v);
    01040
    01041   TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
    01042
    01043   if (v->current_order.IsType(OT_GOTO_DEPOT)) {
    01044     InvalidateWindow(WC_VEHICLE_VIEW, v->index);
    01045
    01046     const Order *real_order = GetVehicleOrder(v, v->cur_order_index);
    01047     Order t = v->current_order;
    01048     v->current_order.MakeDummy();
    01049
    01050     /* Test whether we are heading for this depot. If not, do nothing.
    01051      * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
    01052     if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
    01053         real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
    01054         (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
    01055       /* We are heading for another depot, keep driving. */
    01056       return;
    01057     }
    01058
    01059     if (t.IsRefit()) {
    01060       _current_company = v->owner;
    01061       CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
    01062
    01063       if (CmdFailed(cost)) {
    01064         _vehicles_to_autoreplace[v] = false;
    01065         if (v->owner == _local_company) {
    01066           /* Notify the user that we stopped the vehicle */
    01067           SetDParam(0, v->index);
    01068           AddNewsItem(STR_ORDER_REFIT_FAILED, NS_ADVICE, v->index, 0);
    01069         }
    01070       } else if (v->owner == _local_company && cost.GetCost() != 0) {
    01071         ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
    01072       }
    01073     }
    01074
    01075     if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
    01076       /* Part of orders */
    01077       UpdateVehicleTimetable(v, true);
    01078       v->cur_order_index++;
    01079     }
    01080     if (t.GetDepotActionType() & ODATFB_HALT) {
    01081       /* Vehicles are always stopped on entering depots. Do not restart this one. */
    01082       _vehicles_to_autoreplace[v] = false;
    01083       if (v->owner == _local_company) {
    01084         StringID string;
    01085
    01086         switch (v->type) {
    01087           case VEH_TRAIN:    string = STR_8814_TRAIN_IS_WAITING_IN_DEPOT; break;
    01088           case VEH_ROAD:     string = STR_9016_ROAD_VEHICLE_IS_WAITING;   break;
    01089           case VEH_SHIP:     string = STR_981C_SHIP_IS_WAITING_IN_DEPOT;  break;
    01090           case VEH_AIRCRAFT: string = STR_A014_AIRCRAFT_IS_WAITING_IN;    break;
    01091           default: NOT_REACHED(); string = STR_EMPTY; // Set the string to something to avoid a compiler warning
    01092         }
    01093
    01094         SetDParam(0, v->index);
    01095         AddNewsItem(string, NS_ADVICE, v->index, 0);
    01096       }
    01097       AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
    01098     }
    01099   }
    01100 }
    01101
    01102
    01110 void VehicleMove(Vehicle *v, bool update_viewport)
    01111 {
    01112   int img = v->cur_image;
    01113   Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
    01114   const Sprite *spr = GetSprite(img, ST_NORMAL);
    01115
    01116   pt.x += spr->x_offs;
    01117   pt.y += spr->y_offs;
    01118
    01119   UpdateVehiclePosHash(v, pt.x, pt.y);
    01120
    01121   Rect old_coord = v->coord;
    01122   v->coord.left   = pt.x;
    01123   v->coord.top    = pt.y;
    01124   v->coord.right  = pt.x + spr->width + 2;
    01125   v->coord.bottom = pt.y + spr->height + 2;
    01126
    01127   if (update_viewport) {
    01128     MarkAllViewportsDirty(
    01129       min(old_coord.left,   v->coord.left),
    01130       min(old_coord.top,    v->coord.top),
    01131       max(old_coord.right,  v->coord.right) + 1,
    01132       max(old_coord.bottom, v->coord.bottom) + 1
    01133     );
    01134   }
    01135 }
    01136
    01145 void MarkSingleVehicleDirty(const Vehicle *v)
    01146 {
    01147   MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
    01148 }
    01149
    01154 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
    01155 {
    01156   static const int8 _delta_coord[16] = {
    01157     -1,-1,-1, 0, 1, 1, 1, 0, /* x */
    01158     -1, 0, 1, 1, 1, 0,-1,-1, /* y */
    01159   };
    01160
    01161   int x = v->x_pos + _delta_coord[v->direction];
    01162   int y = v->y_pos + _delta_coord[v->direction + 8];
    01163
    01164   GetNewVehiclePosResult gp;
    01165   gp.x = x;
    01166   gp.y = y;
    01167   gp.old_tile = v->tile;
    01168   gp.new_tile = TileVirtXY(x, y);
    01169   return gp;
    01170 }
    01171
    01172 static const Direction _new_direction_table[] = {
    01173   DIR_N , DIR_NW, DIR_W ,
    01174   DIR_NE, DIR_SE, DIR_SW,
    01175   DIR_E , DIR_SE, DIR_S
    01176 };
    01177
    01178 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
    01179 {
    01180   int i = 0;
    01181
    01182   if (y >= v->y_pos) {
    01183     if (y != v->y_pos) i += 3;
    01184     i += 3;
    01185   }
    01186
    01187   if (x >= v->x_pos) {
    01188     if (x != v->x_pos) i++;
    01189     i++;
    01190   }
    01191
    01192   Direction dir = v->direction;
    01193
    01194   DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
    01195   if (dirdiff == DIRDIFF_SAME) return dir;
    01196   return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
    01197 }
    01198
    01199 Trackdir GetVehicleTrackdir(const Vehicle *v)
    01200 {
    01201   if (v->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
    01202
    01203   switch (v->type) {
    01204     case VEH_TRAIN:
    01205       if (v->u.rail.track == TRACK_BIT_DEPOT) // We'll assume the train is facing outwards
    01206         return DiagDirToDiagTrackdir(GetRailDepotDirection(v->tile)); // Train in depot
    01207
    01208       if (v->u.rail.track == TRACK_BIT_WORMHOLE) // train in tunnel or on bridge, so just use his direction and assume a diagonal track
    01209         return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
    01210
    01211       return TrackDirectionToTrackdir(FindFirstTrack(v->u.rail.track), v->direction);
    01212
    01213     case VEH_SHIP:
    01214       if (v->IsInDepot())
    01215         /* We'll assume the ship is facing outwards */
    01216         return DiagDirToDiagTrackdir(GetShipDepotDirection(v->tile));
    01217
    01218       if (v->u.ship.state == TRACK_BIT_WORMHOLE) // ship on aqueduct, so just use his direction and assume a diagonal track
    01219         return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
    01220
    01221       return TrackDirectionToTrackdir(FindFirstTrack(v->u.ship.state), v->direction);
    01222
    01223     case VEH_ROAD:
    01224       if (v->IsInDepot()) // We'll assume the road vehicle is facing outwards
    01225         return DiagDirToDiagTrackdir(GetRoadDepotDirection(v->tile));
    01226
    01227       if (IsStandardRoadStopTile(v->tile)) // We'll assume the road vehicle is facing outwards
    01228         return DiagDirToDiagTrackdir(GetRoadStopDir(v->tile)); // Road vehicle in a station
    01229
    01230       /* Drive through road stops / wormholes (tunnels) */
    01231       if (v->u.road.state > RVSB_TRACKDIR_MASK) return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
    01232
    01233       /* If vehicle's state is a valid track direction (vehicle is not turning around) return it,
    01234        * otherwise transform it into a valid track direction */
    01235       return (Trackdir)((IsReversingRoadTrackdir((Trackdir)v->u.road.state)) ? (v->u.road.state - 6) : v->u.road.state);
    01236
    01237     /* case VEH_AIRCRAFT: case VEH_EFFECT: case VEH_DISASTER: */
    01238     default: return INVALID_TRACKDIR;
    01239   }
    01240 }
    01241
    01251 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
    01252 {
    01253   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
    01254 }
    01255
    01256 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
    01257 {
    01258   /* Find maximum */
    01259   const Vehicle *v;
    01260   FOR_ALL_VEHICLES(v) {
    01261     if (v->type == type && v->owner == owner) {
    01262       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
    01263     }
    01264   }
    01265
    01266   if (this->maxid == 0) return;
    01267
    01268   this->maxid++; // so there is space for last item (with v->unitnumber == maxid)
    01269   this->maxid++; // this one will always be free (well, it will fail when there are 65535 units, so this overflows)
    01270
    01271   this->cache = CallocT<bool>(this->maxid);
    01272
    01273   /* Fill the cache */
    01274   FOR_ALL_VEHICLES(v) {
    01275     if (v->type == type && v->owner == owner) {
    01276       this->cache[v->unitnumber] = true;
    01277     }
    01278   }
    01279 }
    01280
    01281 UnitID FreeUnitIDGenerator::NextID()
    01282 {
    01283   if (this->maxid <= this->curid) return ++this->curid;
    01284
    01285   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
    01286
    01287   return this->curid;
    01288 }
    01289
    01290 UnitID GetFreeUnitNumber(VehicleType type)
    01291 {
    01292   FreeUnitIDGenerator gen(type, _current_company);
    01293
    01294   return gen.NextID();
    01295 }
    01296
    01297
    01306 bool CanBuildVehicleInfrastructure(VehicleType type)
    01307 {
    01308   assert(IsCompanyBuildableVehicleType(type));
    01309
    01310   if (!IsValidCompanyID(_local_company)) return false;
    01311   if (_settings_client.gui.always_build_infrastructure) return true;
    01312
    01313   UnitID max;
    01314   switch (type) {
    01315     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
    01316     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
    01317     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
    01318     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
    01319     default: NOT_REACHED();
    01320   }
    01321
    01322   /* We can build vehicle infrastructure when we may build the vehicle type */
    01323   if (max > 0) {
    01324     /* Can we actually build the vehicle type? */
    01325     const Engine *e;
    01326     FOR_ALL_ENGINES_OF_TYPE(e, type) {
    01327       if (HasBit(e->company_avail, _local_company)) return true;
    01328     }
    01329     return false;
    01330   }
    01331
    01332   /* We should be able to build infrastructure when we have the actual vehicle type */
    01333   const Vehicle *v;
    01334   FOR_ALL_VEHICLES(v) {
    01335     if (v->owner == _local_company && v->type == type) return true;
    01336   }
    01337
    01338   return false;
    01339 }
    01340
    01341
    01342 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
    01343 {
    01344   const Company *c = GetCompany(company);
    01345   LiveryScheme scheme = LS_DEFAULT;
    01346   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
    01347
    01348   /* The default livery is always available for use, but its in_use flag determines
    01349    * whether any _other_ liveries are in use. */
    01350   if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
    01351     /* Determine the livery scheme to use */
    01352     const Engine *e = GetEngine(engine_type);
    01353     switch (e->type) {
    01354       default: NOT_REACHED();
    01355       case VEH_TRAIN: {
    01356         const RailVehicleInfo *rvi = RailVehInfo(engine_type);
    01357         if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (IsArticulatedPart(v) && rvi->railveh_type != RAILVEH_WAGON))) {
    01358           /* Wagonoverrides use the coloir scheme of the front engine.
    01359            * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
    01360           engine_type = parent_engine_type;
    01361           e = GetEngine(engine_type);
    01362           rvi = RailVehInfo(engine_type);
    01363           /* Note: Luckily cargo_type is not needed for engines */
    01364         }
    01365
    01366         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01367         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01368         if (rvi->railveh_type == RAILVEH_WAGON) {
    01369           if (!GetCargo(cargo_type)->is_freight) {
    01370             if (parent_engine_type == INVALID_ENGINE) {
    01371               scheme = LS_PASSENGER_WAGON_STEAM;
    01372             } else {
    01373               switch (RailVehInfo(parent_engine_type)->engclass) {
    01374                 default: NOT_REACHED();
    01375                 case EC_STEAM:    scheme = LS_PASSENGER_WAGON_STEAM;    break;
    01376                 case EC_DIESEL:   scheme = LS_PASSENGER_WAGON_DIESEL;   break;
    01377                 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
    01378                 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
    01379                 case EC_MAGLEV:   scheme = LS_PASSENGER_WAGON_MAGLEV;   break;
    01380               }
    01381             }
    01382           } else {
    01383             scheme = LS_FREIGHT_WAGON;
    01384           }
    01385         } else {
    01386           bool is_mu = HasBit(EngInfo(engine_type)->misc_flags, EF_RAIL_IS_MU);
    01387
    01388           switch (rvi->engclass) {
    01389             default: NOT_REACHED();
    01390             case EC_STEAM:    scheme = LS_STEAM; break;
    01391             case EC_DIESEL:   scheme = is_mu ? LS_DMU : LS_DIESEL;   break;
    01392             case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
    01393             case EC_MONORAIL: scheme = LS_MONORAIL; break;
    01394             case EC_MAGLEV:   scheme = LS_MAGLEV; break;
    01395           }
    01396         }
    01397         break;
    01398       }
    01399
    01400       case VEH_ROAD: {
    01401         /* Always use the livery of the front */
    01402         if (v != NULL && parent_engine_type != INVALID_ENGINE) {
    01403           engine_type = parent_engine_type;
    01404           e = GetEngine(engine_type);
    01405           cargo_type = v->First()->cargo_type;
    01406         }
    01407         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01408         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01409
    01410         /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
    01411         if (HasBit(EngInfo(engine_type)->misc_flags, EF_ROAD_TRAM)) {
    01412           /* Tram */
    01413           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
    01414         } else {
    01415           /* Bus or truck */
    01416           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
    01417         }
    01418         break;
    01419       }
    01420
    01421       case VEH_SHIP: {
    01422         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01423         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01424         scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
    01425         break;
    01426       }
    01427
    01428       case VEH_AIRCRAFT: {
    01429         switch (e->u.air.subtype) {
    01430           case AIR_HELI: scheme = LS_HELICOPTER; break;
    01431           case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
    01432           case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
    01433         }
    01434         break;
    01435       }
    01436     }
    01437
    01438     /* Switch back to the default scheme if the resolved scheme is not in use */
    01439     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
    01440   }
    01441
    01442   return &c->livery[scheme];
    01443 }
    01444
    01445
    01446 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
    01447 {
    01448   SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
    01449
    01450   /* Return cached value if any */
    01451   if (map != PAL_NONE) return map;
    01452
    01453   /* Check if we should use the colour map callback */
    01454   if (HasBit(EngInfo(engine_type)->callbackmask, CBM_VEHICLE_COLOUR_REMAP)) {
    01455     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
    01456     /* A return value of 0xC000 is stated to "use the default two-colour
    01457      * maps" which happens to be the failure action too... */
    01458     if (callback != CALLBACK_FAILED && callback != 0xC000) {
    01459       map = GB(callback, 0, 14);
    01460       /* If bit 14 is set, then the company colours are applied to the
    01461        * map else it's returned as-is. */
    01462       if (!HasBit(callback, 14)) {
    01463         /* Update cache */
    01464         if (v != NULL) ((Vehicle*)v)->colourmap = map;
    01465         return map;
    01466       }
    01467     }
    01468   }
    01469
    01470   bool twocc = HasBit(EngInfo(engine_type)->misc_flags, EF_USES_2CC);
    01471
    01472   if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
    01473
    01474   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
    01475
    01476   map += livery->colour1;
    01477   if (twocc) map += livery->colour2 * 16;
    01478
    01479   /* Update cache */
    01480   if (v != NULL) ((Vehicle*)v)->colourmap = map;
    01481   return map;
    01482 }
    01483
    01484 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
    01485 {
    01486   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
    01487 }
    01488
    01489 SpriteID GetVehiclePalette(const Vehicle *v)
    01490 {
    01491   if (v->type == VEH_TRAIN) {
    01492     return GetEngineColourMap(v->engine_type, v->owner, v->u.rail.first_engine, v);
    01493   } else if (v->type == VEH_ROAD) {
    01494     return GetEngineColourMap(v->engine_type, v->owner, v->u.road.first_engine, v);
    01495   }
    01496
    01497   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
    01498 }
    01499
    01500
    01501 void Vehicle::BeginLoading()
    01502 {
    01503   assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
    01504
    01505   if (this->current_order.IsType(OT_GOTO_STATION) &&
    01506       this->current_order.GetDestination() == this->last_station_visited) {
    01507     current_order.MakeLoading(true);
    01508     UpdateVehicleTimetable(this, true);
    01509
    01510     /* Furthermore add the Non Stop flag to mark that this station
    01511      * is the actual destination of the vehicle, which is (for example)
    01512      * necessary to be known for HandleTrainLoading to determine
    01513      * whether the train is lost or not; not marking a train lost
    01514      * that arrives at random stations is bad. */
    01515     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
    01516
    01517   } else {
    01518     current_order.MakeLoading(false);
    01519   }
    01520
    01521   GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
    01522
    01523   PrepareUnload(this);
    01524
    01525   InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner);
    01526   InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01527   InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
    01528   InvalidateWindow(WC_STATION_VIEW, this->last_station_visited);
    01529
    01530   GetStation(this->last_station_visited)->MarkTilesDirty(true);
    01531   this->cur_speed = 0;
    01532   this->MarkDirty();
    01533 }
    01534
    01535 void Vehicle::LeaveStation()
    01536 {
    01537   assert(current_order.IsType(OT_LOADING));
    01538
    01539   delete this->cargo_payment;
    01540
    01541   /* Only update the timetable if the vehicle was supposed to stop here. */
    01542   if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
    01543
    01544   current_order.MakeLeaveStation();
    01545   Station *st = GetStation(this->last_station_visited);
    01546   st->loading_vehicles.remove(this);
    01547
    01548   HideFillingPercent(&this->fill_percent_te_id);
    01549
    01550   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
    01551     /* Trigger station animation (trains only) */
    01552     if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
    01553
    01554     /* Try to reserve a path when leaving the station as we
    01555      * might not be marked as wanting a reservation, e.g.
    01556      * when an overlength train gets turned around in a station. */
    01557     if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(GetVehicleTrackdir(this)), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
    01558       TryPathReserve(this, true, true);
    01559     }
    01560   }
    01561 }
    01562
    01563
    01564 void Vehicle::HandleLoading(bool mode)
    01565 {
    01566   switch (this->current_order.GetType()) {
    01567     case OT_LOADING: {
    01568       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
    01569
    01570       /* Not the first call for this tick, or still loading */
    01571       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
    01572           (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
    01573
    01574       this->PlayLeaveStationSound();
    01575
    01576       bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
    01577       this->LeaveStation();
    01578
    01579       /* If this was not the final order, don't remove it from the list. */
    01580       if (!at_destination_station) return;
    01581       break;
    01582     }
    01583
    01584     case OT_DUMMY: break;
    01585
    01586     default: return;
    01587   }
    01588
    01589   this->cur_order_index++;
    01590   InvalidateVehicleOrder(this, 0);
    01591 }
    01592
    01593 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
    01594 {
    01595   if (!CheckOwnership(this->owner)) return CMD_ERROR;
    01596   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
    01597   if (this->IsStoppedInDepot()) return CMD_ERROR;
    01598
    01599   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
    01600     bool halt_in_depot = this->current_order.GetDepotActionType() & ODATFB_HALT;
    01601     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
    01602       /* We called with a different DEPOT_SERVICE setting.
    01603        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
    01604        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
    01605       if (flags & DC_EXEC) {
    01606         this->current_order.SetDepotOrderType(ODTF_MANUAL);
    01607         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
    01608         InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01609       }
    01610       return CommandCost();
    01611     }
    01612
    01613     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
    01614     if (flags & DC_EXEC) {
    01615       /* If the orders to 'goto depot' are in the orders list (forced servicing),
    01616        * then skip to the next order; effectively cancelling this forced service */
    01617       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->cur_order_index++;
    01618
    01619       this->current_order.MakeDummy();
    01620       InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01621     }
    01622     return CommandCost();
    01623   }
    01624
    01625   TileIndex location;
    01626   DestinationID destination;
    01627   bool reverse;
    01628   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};
    01629   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
    01630
    01631   if (flags & DC_EXEC) {
    01632     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
    01633
    01634     this->dest_tile = location;
    01635     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
    01636     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
    01637     InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01638
    01639     /* If there is no depot in front, reverse automatically (trains only) */
    01640     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
    01641
    01642     if (this->type == VEH_AIRCRAFT && this->u.air.state == FLYING && this->u.air.targetairport != destination) {
    01643       /* The aircraft is now heading for a different hangar than the next in the orders */
    01644       extern void AircraftNextAirportPos_and_Order(Vehicle *v);
    01645       AircraftNextAirportPos_and_Order(this);
    01646     }
    01647   }
    01648
    01649   return CommandCost();
    01650
    01651 }
    01652
    01653 void Vehicle::SetNext(Vehicle *next)
    01654 {
    01655   if (this->next != NULL) {
    01656     /* We had an old next vehicle. Update the first and previous pointers */
    01657     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    01658       v->first = this->next;
    01659     }
    01660     this->next->previous = NULL;
    01661   }
    01662
    01663   this->next = next;
    01664
    01665   if (this->next != NULL) {
    01666     /* A new next vehicle. Update the first and previous pointers */
    01667     if (this->next->previous != NULL) this->next->previous->next = NULL;
    01668     this->next->previous = this;
    01669     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    01670       v->first = this->first;
    01671     }
    01672   }
    01673 }
    01674
    01675 void Vehicle::AddToShared(Vehicle *shared_chain)
    01676 {
    01677   assert(this->previous_shared == NULL && this->next_shared == NULL);
    01678
    01679   if (!shared_chain->orders.list) {
    01680     assert(shared_chain->previous_shared == NULL);
    01681     assert(shared_chain->next_shared == NULL);
    01682     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
    01683   }
    01684
    01685   this->next_shared     = shared_chain->next_shared;
    01686   this->previous_shared = shared_chain;
    01687
    01688   shared_chain->next_shared = this;
    01689
    01690   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
    01691
    01692   shared_chain->orders.list->AddVehicle(this);
    01693 }
    01694
    01695 void Vehicle::RemoveFromShared()
    01696 {
    01697   /* Remember if we were first and the old window number before RemoveVehicle()
    01698    * as this changes first if needed. */
    01699   bool were_first = (this->FirstShared() == this);
    01700   uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
    01701
    01702   this->orders.list->RemoveVehicle(this);
    01703
    01704   if (!were_first) {
    01705     /* We are not the first shared one, so only relink our previous one. */
    01706     this->previous_shared->next_shared = this->NextShared();
    01707   }
    01708
    01709   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
    01710
    01711
    01712   if (this->orders.list->GetNumVehicles() == 1) {
    01713     /* When there is only one vehicle, remove the shared order list window. */
    01714     DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
    01715     InvalidateVehicleOrder(this->FirstShared(), 0);
    01716   } else if (were_first) {
    01717     /* If we were the first one, update to the new first one.
    01718      * Note: FirstShared() is already the new first */
    01719     InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
    01720   }
    01721
    01722   this->next_shared     = NULL;
    01723   this->previous_shared = NULL;
    01724 }
    01725
    01726 void StopAllVehicles()
    01727 {
    01728   Vehicle *v;
    01729   FOR_ALL_VEHICLES(v) {
    01730     /* Code ripped from CmdStartStopTrain. Can't call it, because of
    01731      * ownership problems, so we'll duplicate some code, for now */
    01732     v->vehstatus |= VS_STOPPED;
    01733     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    01734     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
    01735   }
    01736 }
    01737
    01738 void VehiclesYearlyLoop()
    01739 {
    01740   Vehicle *v;
    01741   FOR_ALL_VEHICLES(v) {
    01742     if (v->IsPrimaryVehicle()) {
    01743       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
    01744       Money profit = v->GetDisplayProfitThisYear();
    01745       if (v->age >= 730 && profit < 0) {
    01746         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
    01747           SetDParam(0, v->index);
    01748           SetDParam(1, profit);
    01749           AddNewsItem(
    01750             STR_VEHICLE_IS_UNPROFITABLE,
    01751             NS_ADVICE,
    01752             v->index,
    01753             0);
    01754         }
    01755         AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
    01756       }
    01757
    01758       v->profit_last_year = v->profit_this_year;
    01759       v->profit_this_year = 0;
    01760       InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    01761     }
    01762   }
    01763 }
    01764
    01765
    01775 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
    01776 {
    01777   assert(IsEngineIndex(engine_type));
    01778   const Engine *e = GetEngine(engine_type);
    01779
    01780   switch (e->type) {
    01781     case VEH_TRAIN:
    01782       return (st->facilities & FACIL_TRAIN) != 0;
    01783
    01784     case VEH_ROAD:
    01785       /* For road vehicles we need the vehicle to know whether it can actually
    01786        * use the station, but if it doesn't have facilities for RVs it is
    01787        * certainly not possible that the station can be used. */
    01788       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
    01789
    01790     case VEH_SHIP:
    01791       return (st->facilities & FACIL_DOCK) != 0;
    01792
    01793     case VEH_AIRCRAFT:
    01794       return (st->facilities & FACIL_AIRPORT) != 0 &&
    01795           (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
    01796
    01797     default:
    01798       return false;
    01799   }
    01800 }
    01801
    01808 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
    01809 {
    01810   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(v) != NULL;
    01811
    01812   return CanVehicleUseStation(v->engine_type, st);
    01813 }