# vehicle.cpp

Go to the documentation of this file.

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