# vehicle.cpp

Go to the documentation of this file.

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