# vehicle.cpp

Go to the documentation of this file.

    00001 /* $Id: vehicle.cpp 16310 2009-05-15 10:24:13Z 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       /* Drive through road stops / wormholes (tunnels) */
    01223       if (v->u.road.state > RVSB_TRACKDIR_MASK) return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
    01224
    01225       /* If vehicle's state is a valid track direction (vehicle is not turning around) return it,
    01226        * otherwise transform it into a valid track direction */
    01227       return (Trackdir)((IsReversingRoadTrackdir((Trackdir)v->u.road.state)) ? (v->u.road.state - 6) : v->u.road.state);
    01228
    01229     /* case VEH_AIRCRAFT: case VEH_EFFECT: case VEH_DISASTER: */
    01230     default: return INVALID_TRACKDIR;
    01231   }
    01232 }
    01233
    01243 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
    01244 {
    01245   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
    01246 }
    01247
    01248 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
    01249 {
    01250   /* Find maximum */
    01251   const Vehicle *v;
    01252   FOR_ALL_VEHICLES(v) {
    01253     if (v->type == type && v->owner == owner) {
    01254       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
    01255     }
    01256   }
    01257
    01258   if (this->maxid == 0) return;
    01259
    01260   this->maxid++; // so there is space for last item (with v->unitnumber == maxid)
    01261   this->maxid++; // this one will always be free (well, it will fail when there are 65535 units, so this overflows)
    01262
    01263   this->cache = CallocT<bool>(this->maxid);
    01264
    01265   /* Fill the cache */
    01266   FOR_ALL_VEHICLES(v) {
    01267     if (v->type == type && v->owner == owner) {
    01268       this->cache[v->unitnumber] = true;
    01269     }
    01270   }
    01271 }
    01272
    01273 UnitID FreeUnitIDGenerator::NextID()
    01274 {
    01275   if (this->maxid <= this->curid) return ++this->curid;
    01276
    01277   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
    01278
    01279   return this->curid;
    01280 }
    01281
    01282 UnitID GetFreeUnitNumber(VehicleType type)
    01283 {
    01284   FreeUnitIDGenerator gen(type, _current_company);
    01285
    01286   return gen.NextID();
    01287 }
    01288
    01289
    01298 bool CanBuildVehicleInfrastructure(VehicleType type)
    01299 {
    01300   assert(IsCompanyBuildableVehicleType(type));
    01301
    01302   if (!IsValidCompanyID(_local_company)) return false;
    01303   if (_settings_client.gui.always_build_infrastructure) return true;
    01304
    01305   UnitID max;
    01306   switch (type) {
    01307     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
    01308     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
    01309     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
    01310     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
    01311     default: NOT_REACHED();
    01312   }
    01313
    01314   /* We can build vehicle infrastructure when we may build the vehicle type */
    01315   if (max > 0) {
    01316     /* Can we actually build the vehicle type? */
    01317     const Engine *e;
    01318     FOR_ALL_ENGINES_OF_TYPE(e, type) {
    01319       if (HasBit(e->company_avail, _local_company)) return true;
    01320     }
    01321     return false;
    01322   }
    01323
    01324   /* We should be able to build infrastructure when we have the actual vehicle type */
    01325   const Vehicle *v;
    01326   FOR_ALL_VEHICLES(v) {
    01327     if (v->owner == _local_company && v->type == type) return true;
    01328   }
    01329
    01330   return false;
    01331 }
    01332
    01333
    01334 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
    01335 {
    01336   const Company *c = GetCompany(company);
    01337   LiveryScheme scheme = LS_DEFAULT;
    01338   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
    01339
    01340   /* The default livery is always available for use, but its in_use flag determines
    01341    * whether any _other_ liveries are in use. */
    01342   if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
    01343     /* Determine the livery scheme to use */
    01344     const Engine *e = GetEngine(engine_type);
    01345     switch (e->type) {
    01346       default: NOT_REACHED();
    01347       case VEH_TRAIN: {
    01348         const RailVehicleInfo *rvi = RailVehInfo(engine_type);
    01349         if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (IsArticulatedPart(v) && rvi->railveh_type != RAILVEH_WAGON))) {
    01350           /* Wagonoverrides use the coloir scheme of the front engine.
    01351            * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
    01352           engine_type = parent_engine_type;
    01353           e = GetEngine(engine_type);
    01354           rvi = RailVehInfo(engine_type);
    01355           /* Note: Luckily cargo_type is not needed for engines */
    01356         }
    01357
    01358         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01359         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01360         if (rvi->railveh_type == RAILVEH_WAGON) {
    01361           if (!GetCargo(cargo_type)->is_freight) {
    01362             if (parent_engine_type == INVALID_ENGINE) {
    01363               scheme = LS_PASSENGER_WAGON_STEAM;
    01364             } else {
    01365               switch (RailVehInfo(parent_engine_type)->engclass) {
    01366                 default: NOT_REACHED();
    01367                 case EC_STEAM:    scheme = LS_PASSENGER_WAGON_STEAM;    break;
    01368                 case EC_DIESEL:   scheme = LS_PASSENGER_WAGON_DIESEL;   break;
    01369                 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
    01370                 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
    01371                 case EC_MAGLEV:   scheme = LS_PASSENGER_WAGON_MAGLEV;   break;
    01372               }
    01373             }
    01374           } else {
    01375             scheme = LS_FREIGHT_WAGON;
    01376           }
    01377         } else {
    01378           bool is_mu = HasBit(EngInfo(engine_type)->misc_flags, EF_RAIL_IS_MU);
    01379
    01380           switch (rvi->engclass) {
    01381             default: NOT_REACHED();
    01382             case EC_STEAM:    scheme = LS_STEAM; break;
    01383             case EC_DIESEL:   scheme = is_mu ? LS_DMU : LS_DIESEL;   break;
    01384             case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
    01385             case EC_MONORAIL: scheme = LS_MONORAIL; break;
    01386             case EC_MAGLEV:   scheme = LS_MAGLEV; break;
    01387           }
    01388         }
    01389         break;
    01390       }
    01391
    01392       case VEH_ROAD: {
    01393         /* Always use the livery of the front */
    01394         if (v != NULL && parent_engine_type != INVALID_ENGINE) {
    01395           engine_type = parent_engine_type;
    01396           e = GetEngine(engine_type);
    01397           cargo_type = v->First()->cargo_type;
    01398         }
    01399         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01400         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01401
    01402         /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
    01403         if (HasBit(EngInfo(engine_type)->misc_flags, EF_ROAD_TRAM)) {
    01404           /* Tram */
    01405           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
    01406         } else {
    01407           /* Bus or truck */
    01408           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
    01409         }
    01410         break;
    01411       }
    01412
    01413       case VEH_SHIP: {
    01414         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01415         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01416         scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
    01417         break;
    01418       }
    01419
    01420       case VEH_AIRCRAFT: {
    01421         switch (e->u.air.subtype) {
    01422           case AIR_HELI: scheme = LS_HELICOPTER; break;
    01423           case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
    01424           case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
    01425         }
    01426         break;
    01427       }
    01428     }
    01429
    01430     /* Switch back to the default scheme if the resolved scheme is not in use */
    01431     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
    01432   }
    01433
    01434   return &c->livery[scheme];
    01435 }
    01436
    01437
    01438 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
    01439 {
    01440   SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
    01441
    01442   /* Return cached value if any */
    01443   if (map != PAL_NONE) return map;
    01444
    01445   /* Check if we should use the colour map callback */
    01446   if (HasBit(EngInfo(engine_type)->callbackmask, CBM_VEHICLE_COLOUR_REMAP)) {
    01447     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
    01448     /* A return value of 0xC000 is stated to "use the default two-colour
    01449      * maps" which happens to be the failure action too... */
    01450     if (callback != CALLBACK_FAILED && callback != 0xC000) {
    01451       map = GB(callback, 0, 14);
    01452       /* If bit 14 is set, then the company colours are applied to the
    01453        * map else it's returned as-is. */
    01454       if (!HasBit(callback, 14)) {
    01455         /* Update cache */
    01456         if (v != NULL) ((Vehicle*)v)->colourmap = map;
    01457         return map;
    01458       }
    01459     }
    01460   }
    01461
    01462   bool twocc = HasBit(EngInfo(engine_type)->misc_flags, EF_USES_2CC);
    01463
    01464   if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
    01465
    01466   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
    01467
    01468   map += livery->colour1;
    01469   if (twocc) map += livery->colour2 * 16;
    01470
    01471   /* Update cache */
    01472   if (v != NULL) ((Vehicle*)v)->colourmap = map;
    01473   return map;
    01474 }
    01475
    01476 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
    01477 {
    01478   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
    01479 }
    01480
    01481 SpriteID GetVehiclePalette(const Vehicle *v)
    01482 {
    01483   if (v->type == VEH_TRAIN) {
    01484     return GetEngineColourMap(v->engine_type, v->owner, v->u.rail.first_engine, v);
    01485   } else if (v->type == VEH_ROAD) {
    01486     return GetEngineColourMap(v->engine_type, v->owner, v->u.road.first_engine, v);
    01487   }
    01488
    01489   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
    01490 }
    01491
    01492
    01493 void Vehicle::BeginLoading()
    01494 {
    01495   assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
    01496
    01497   if (this->current_order.IsType(OT_GOTO_STATION) &&
    01498       this->current_order.GetDestination() == this->last_station_visited) {
    01499     current_order.MakeLoading(true);
    01500     UpdateVehicleTimetable(this, true);
    01501
    01502     /* Furthermore add the Non Stop flag to mark that this station
    01503      * is the actual destination of the vehicle, which is (for example)
    01504      * necessary to be known for HandleTrainLoading to determine
    01505      * whether the train is lost or not; not marking a train lost
    01506      * that arrives at random stations is bad. */
    01507     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
    01508
    01509   } else {
    01510     current_order.MakeLoading(false);
    01511   }
    01512
    01513   GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
    01514
    01515   VehiclePayment(this);
    01516
    01517   InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner);
    01518   InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01519   InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
    01520   InvalidateWindow(WC_STATION_VIEW, this->last_station_visited);
    01521
    01522   GetStation(this->last_station_visited)->MarkTilesDirty(true);
    01523   this->cur_speed = 0;
    01524   this->MarkDirty();
    01525 }
    01526
    01527 void Vehicle::LeaveStation()
    01528 {
    01529   assert(current_order.IsType(OT_LOADING));
    01530
    01531   /* Only update the timetable if the vehicle was supposed to stop here. */
    01532   if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
    01533
    01534   current_order.MakeLeaveStation();
    01535   Station *st = GetStation(this->last_station_visited);
    01536   st->loading_vehicles.remove(this);
    01537
    01538   HideFillingPercent(&this->fill_percent_te_id);
    01539
    01540   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
    01541     /* Trigger station animation (trains only) */
    01542     if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
    01543
    01544     /* Try to reserve a path when leaving the station as we
    01545      * might not be marked as wanting a reservation, e.g.
    01546      * when an overlength train gets turned around in a station. */
    01547     if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(GetVehicleTrackdir(this)), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
    01548       TryPathReserve(this, true, true);
    01549     }
    01550   }
    01551 }
    01552
    01553
    01554 void Vehicle::HandleLoading(bool mode)
    01555 {
    01556   switch (this->current_order.GetType()) {
    01557     case OT_LOADING: {
    01558       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
    01559
    01560       /* Not the first call for this tick, or still loading */
    01561       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
    01562           (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
    01563
    01564       this->PlayLeaveStationSound();
    01565
    01566       bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
    01567       this->LeaveStation();
    01568
    01569       /* If this was not the final order, don't remove it from the list. */
    01570       if (!at_destination_station) return;
    01571       break;
    01572     }
    01573
    01574     case OT_DUMMY: break;
    01575
    01576     default: return;
    01577   }
    01578
    01579   this->cur_order_index++;
    01580   InvalidateVehicleOrder(this, 0);
    01581 }
    01582
    01583 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
    01584 {
    01585   if (!CheckOwnership(this->owner)) return CMD_ERROR;
    01586   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
    01587   if (this->IsStoppedInDepot()) return CMD_ERROR;
    01588
    01589   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
    01590     bool halt_in_depot = this->current_order.GetDepotActionType() & ODATFB_HALT;
    01591     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
    01592       /* We called with a different DEPOT_SERVICE setting.
    01593        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
    01594        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
    01595       if (flags & DC_EXEC) {
    01596         this->current_order.SetDepotOrderType(ODTF_MANUAL);
    01597         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
    01598         InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01599       }
    01600       return CommandCost();
    01601     }
    01602
    01603     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
    01604     if (flags & DC_EXEC) {
    01605       /* If the orders to 'goto depot' are in the orders list (forced servicing),
    01606        * then skip to the next order; effectively cancelling this forced service */
    01607       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->cur_order_index++;
    01608
    01609       this->current_order.MakeDummy();
    01610       InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01611     }
    01612     return CommandCost();
    01613   }
    01614
    01615   TileIndex location;
    01616   DestinationID destination;
    01617   bool reverse;
    01618   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};
    01619   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
    01620
    01621   if (flags & DC_EXEC) {
    01622     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
    01623
    01624     this->dest_tile = location;
    01625     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
    01626     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
    01627     InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01628
    01629     /* If there is no depot in front, reverse automatically (trains only) */
    01630     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
    01631
    01632     if (this->type == VEH_AIRCRAFT && this->u.air.state == FLYING && this->u.air.targetairport != destination) {
    01633       /* The aircraft is now heading for a different hangar than the next in the orders */
    01634       extern void AircraftNextAirportPos_and_Order(Vehicle *v);
    01635       AircraftNextAirportPos_and_Order(this);
    01636     }
    01637   }
    01638
    01639   return CommandCost();
    01640
    01641 }
    01642
    01643 void Vehicle::SetNext(Vehicle *next)
    01644 {
    01645   if (this->next != NULL) {
    01646     /* We had an old next vehicle. Update the first and previous pointers */
    01647     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    01648       v->first = this->next;
    01649     }
    01650     this->next->previous = NULL;
    01651   }
    01652
    01653   this->next = next;
    01654
    01655   if (this->next != NULL) {
    01656     /* A new next vehicle. Update the first and previous pointers */
    01657     if (this->next->previous != NULL) this->next->previous->next = NULL;
    01658     this->next->previous = this;
    01659     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    01660       v->first = this->first;
    01661     }
    01662   }
    01663 }
    01664
    01665 void Vehicle::AddToShared(Vehicle *shared_chain)
    01666 {
    01667   assert(this->previous_shared == NULL && this->next_shared == NULL);
    01668
    01669   if (!shared_chain->orders.list) {
    01670     assert(shared_chain->previous_shared == NULL);
    01671     assert(shared_chain->next_shared == NULL);
    01672     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
    01673   }
    01674
    01675   this->next_shared     = shared_chain->next_shared;
    01676   this->previous_shared = shared_chain;
    01677
    01678   shared_chain->next_shared = this;
    01679
    01680   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
    01681
    01682   shared_chain->orders.list->AddVehicle(this);
    01683 }
    01684
    01685 void Vehicle::RemoveFromShared()
    01686 {
    01687   /* Remember if we were first and the old window number before RemoveVehicle()
    01688    * as this changes first if needed. */
    01689   bool were_first = (this->FirstShared() == this);
    01690   uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
    01691
    01692   this->orders.list->RemoveVehicle(this);
    01693
    01694   if (!were_first) {
    01695     /* We are not the first shared one, so only relink our previous one. */
    01696     this->previous_shared->next_shared = this->NextShared();
    01697   }
    01698
    01699   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
    01700
    01701
    01702   if (this->orders.list->GetNumVehicles() == 1) {
    01703     /* When there is only one vehicle, remove the shared order list window. */
    01704     DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
    01705     InvalidateVehicleOrder(this->FirstShared(), 0);
    01706   } else if (were_first) {
    01707     /* If we were the first one, update to the new first one.
    01708      * Note: FirstShared() is already the new first */
    01709     InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
    01710   }
    01711
    01712   this->next_shared     = NULL;
    01713   this->previous_shared = NULL;
    01714 }
    01715
    01716 void StopAllVehicles()
    01717 {
    01718   Vehicle *v;
    01719   FOR_ALL_VEHICLES(v) {
    01720     /* Code ripped from CmdStartStopTrain. Can't call it, because of
    01721      * ownership problems, so we'll duplicate some code, for now */
    01722     v->vehstatus |= VS_STOPPED;
    01723     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    01724     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
    01725   }
    01726 }
    01727
    01728 void VehiclesYearlyLoop()
    01729 {
    01730   Vehicle *v;
    01731   FOR_ALL_VEHICLES(v) {
    01732     if (v->IsPrimaryVehicle()) {
    01733       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
    01734       Money profit = v->GetDisplayProfitThisYear();
    01735       if (v->age >= 730 && profit < 0) {
    01736         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
    01737           SetDParam(0, v->index);
    01738           SetDParam(1, profit);
    01739           AddNewsItem(
    01740             STR_VEHICLE_IS_UNPROFITABLE,
    01741             NS_ADVICE,
    01742             v->index,
    01743             0);
    01744         }
    01745         AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
    01746       }
    01747
    01748       v->profit_last_year = v->profit_this_year;
    01749       v->profit_this_year = 0;
    01750       InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    01751     }
    01752   }
    01753 }
    01754
    01755
    01765 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
    01766 {
    01767   assert(IsEngineIndex(engine_type));
    01768   const Engine *e = GetEngine(engine_type);
    01769
    01770   switch (e->type) {
    01771     case VEH_TRAIN:
    01772       return (st->facilities & FACIL_TRAIN) != 0;
    01773
    01774     case VEH_ROAD:
    01775       /* For road vehicles we need the vehicle to know whether it can actually
    01776        * use the station, but if it doesn't have facilities for RVs it is
    01777        * certainly not possible that the station can be used. */
    01778       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
    01779
    01780     case VEH_SHIP:
    01781       return (st->facilities & FACIL_DOCK) != 0;
    01782
    01783     case VEH_AIRCRAFT:
    01784       return (st->facilities & FACIL_AIRPORT) != 0 &&
    01785           (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
    01786
    01787     default:
    01788       return false;
    01789   }
    01790 }
    01791
    01798 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
    01799 {
    01800   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(v) != NULL;
    01801
    01802   return CanVehicleUseStation(v->engine_type, st);
    01803 }