# vehicle.cpp

Go to the documentation of this file.

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