# vehicle.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "gui.h"
    00014 #include "openttd.h"
    00015 #include "debug.h"
    00016 #include "roadveh.h"
    00017 #include "ship.h"
    00018 #include "spritecache.h"
    00019 #include "landscape.h"
    00020 #include "timetable.h"
    00021 #include "viewport_func.h"
    00022 #include "news_func.h"
    00023 #include "command_func.h"
    00024 #include "company_func.h"
    00025 #include "vehicle_gui.h"
    00026 #include "train.h"
    00027 #include "aircraft.h"
    00028 #include "newgrf_engine.h"
    00029 #include "newgrf_sound.h"
    00030 #include "newgrf_station.h"
    00031 #include "group.h"
    00032 #include "group_gui.h"
    00033 #include "strings_func.h"
    00034 #include "zoom_func.h"
    00035 #include "functions.h"
    00036 #include "date_func.h"
    00037 #include "window_func.h"
    00038 #include "vehicle_func.h"
    00039 #include "autoreplace_func.h"
    00040 #include "autoreplace_gui.h"
    00041 #include "station_base.h"
    00042 #include "ai/ai.hpp"
    00043 #include "core/smallmap_type.hpp"
    00044 #include "depot_func.h"
    00045 #include "network/network.h"
    00046 #include "core/pool_func.hpp"
    00047 #include "economy_base.h"
    00048 #include "articulated_vehicles.h"
    00049 #include "roadstop_base.h"
    00050 #include "infrastructure_func.h"
    00051
    00052 #include "table/sprites.h"
    00053 #include "table/strings.h"
    00054
    00055 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
    00056
    00057 VehicleID _vehicle_id_ctr_day;
    00058 const Vehicle *_place_clicked_vehicle;
    00059 VehicleID _new_vehicle_id;
    00060 uint16 _returned_refit_capacity;
    00061 byte _age_cargo_skip_counter;
    00062
    00063
    00064 /* Initialize the vehicle-pool */
    00065 VehiclePool _vehicle_pool("Vehicle");
    00066 INSTANTIATE_POOL_METHODS(Vehicle)
    00067
    00068
    00072 bool Vehicle::NeedsAutorenewing(const Company *c) const
    00073 {
    00074   /* We can always generate the Company pointer when we have the vehicle.
    00075    * However this takes time and since the Company pointer is often present
    00076    * when this function is called then it's faster to pass the pointer as an
    00077    * argument rather than finding it again. */
    00078   assert(c == Company::Get(this->owner));
    00079
    00080   if (!c->settings.engine_renew) return false;
    00081   if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
    00082   if (this->age == 0) return false; // rail cars don't age and lacks a max age
    00083
    00084   return true;
    00085 }
    00086
    00087 void VehicleServiceInDepot(Vehicle *v)
    00088 {
    00089   v->date_of_last_service = _date;
    00090   v->breakdowns_since_last_service = 0;
    00091   v->reliability = Engine::Get(v->engine_type)->reliability;
    00092   SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
    00093 }
    00094
    00095 bool Vehicle::NeedsServicing() const
    00096 {
    00097   /* Stopped or crashed vehicles will not move, as such making unmovable
    00098    * vehicles to go for service is lame. */
    00099   if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
    00100
    00101   /* Are we ready for the next service cycle? */
    00102   const Company *c = Company::Get(this->owner);
    00103   if (c->settings.vehicle.servint_ispercent ?
    00104       (this->reliability >= Engine::Get(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
    00105       (this->date_of_last_service + this->service_interval >= _date)) {
    00106     return false;
    00107   }
    00108
    00109   /* If we're servicing anyway, because we have not disabled servicing when
    00110    * there are no breakdowns or we are playing with breakdowns, bail out. */
    00111   if (!_settings_game.order.no_servicing_if_no_breakdowns ||
    00112       _settings_game.difficulty.vehicle_breakdowns != 0) {
    00113     return true;
    00114   }
    00115
    00116   /* Test whether there is some pending autoreplace.
    00117    * Note: We do this after the service-interval test.
    00118    * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
    00119   bool pending_replace = false;
    00120   Money needed_money = c->settings.engine_renew_money;
    00121   if (needed_money > c->money) return false;
    00122
    00123   for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
    00124     EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
    00125
    00126     /* Check engine availability */
    00127     if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
    00128
    00129     /* Check refittability */
    00130     uint32 available_cargo_types, union_mask;
    00131     GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
    00132     /* Is there anything to refit? */
    00133     if (union_mask != 0) {
    00134       CargoID cargo_type;
    00135       /* We cannot refit to mixed cargos in an automated way */
    00136       if (IsArticulatedVehicleCarryingDifferentCargos(v, &cargo_type)) continue;
    00137
    00138       /* Did the old vehicle carry anything? */
    00139       if (cargo_type != CT_INVALID) {
    00140         /* We can't refit the vehicle to carry the cargo we want */
    00141         if (!HasBit(available_cargo_types, cargo_type)) continue;
    00142       }
    00143     }
    00144
    00145     /* Check money.
    00146      * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
    00147     pending_replace = true;
    00148     needed_money += 2 * Engine::Get(new_engine)->GetCost();
    00149     if (needed_money > c->money) return false;
    00150   }
    00151
    00152   return pending_replace;
    00153 }
    00154
    00155 bool Vehicle::NeedsAutomaticServicing() const
    00156 {
    00157   if (_settings_game.order.gotodepot && VehicleHasDepotOrders(this)) return false;
    00158   if (this->current_order.IsType(OT_LOADING))            return false;
    00159   if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
    00160   return NeedsServicing();
    00161 }
    00162
    00163 uint Vehicle::Crash(bool flooded)
    00164 {
    00165   assert((this->vehstatus & VS_CRASHED) == 0);
    00166   assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
    00167
    00168   uint pass = 0;
    00169   /* crash all wagons, and count passengers */
    00170   for (Vehicle *v = this; v != NULL; v = v->Next()) {
    00171     if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
    00172     v->vehstatus |= VS_CRASHED;
    00173     MarkSingleVehicleDirty(v);
    00174   }
    00175
    00176   /* Dirty some windows */
    00177   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
    00178   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    00179   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
    00180   SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
    00181
    00182   return pass;
    00183 }
    00184
    00185
    00194 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
    00195 {
    00196   const Engine *e = Engine::Get(engine);
    00197   uint32 grfid = e->grffile->grfid;
    00198   GRFConfig *grfconfig = GetGRFConfig(grfid);
    00199
    00200   if (!HasBit(grfconfig->grf_bugs, bug_type)) {
    00201     SetBit(grfconfig->grf_bugs, bug_type);
    00202     SetDParamStr(0, grfconfig->name);
    00203     SetDParam(1, engine);
    00204     ShowErrorMessage(part1, part2, 0, 0, true);
    00205     if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
    00206   }
    00207
    00208   /* debug output */
    00209   char buffer[512];
    00210
    00211   SetDParamStr(0, grfconfig->name);
    00212   GetString(buffer, part1, lastof(buffer));
    00213   DEBUG(grf, 0, "%s", buffer + 3);
    00214
    00215   SetDParam(1, engine);
    00216   GetString(buffer, part2, lastof(buffer));
    00217   DEBUG(grf, 0, "%s", buffer + 3);
    00218 }
    00219
    00220 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
    00221 {
    00222   byte z = *(byte*)data;
    00223
    00224   if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
    00225   if (v->z_pos > z) return NULL;
    00226
    00227   _error_message = STR_ERROR_TRAIN_IN_THE_WAY + v->type;
    00228   return v;
    00229 }
    00230
    00231 bool EnsureNoVehicleOnGround(TileIndex tile)
    00232 {
    00233   byte z = GetTileMaxZ(tile);
    00234   return !HasVehicleOnPos(tile, &z, &EnsureNoVehicleProcZ);
    00235 }
    00236
    00238 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
    00239 {
    00240   if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
    00241   if (v == (const Vehicle *)data) return NULL;
    00242
    00243   _error_message = STR_ERROR_TRAIN_IN_THE_WAY + v->type;
    00244   return v;
    00245 }
    00246
    00254 bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
    00255 {
    00256   return HasVehicleOnPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc) ||
    00257       HasVehicleOnPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc);
    00258 }
    00259
    00260
    00261 Vehicle::Vehicle(VehicleType type)
    00262 {
    00263   this->type               = type;
    00264   this->coord.left         = INVALID_COORD;
    00265   this->group_id           = DEFAULT_GROUP;
    00266   this->fill_percent_te_id = INVALID_TE_ID;
    00267   this->first              = this;
    00268   this->colourmap          = PAL_NONE;
    00269 }
    00270
    00275 byte VehicleRandomBits()
    00276 {
    00277   return GB(Random(), 0, 8);
    00278 }
    00279
    00280 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
    00281  * lookup times at the expense of memory usage. */
    00282 const int HASH_BITS = 7;
    00283 const int HASH_SIZE = 1 << HASH_BITS;
    00284 const int HASH_MASK = HASH_SIZE - 1;
    00285 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
    00286 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
    00287
    00288 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
    00289  * Profiling results show that 0 is fastest. */
    00290 const int HASH_RES = 0;
    00291
    00292 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
    00293
    00294 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
    00295 {
    00296   for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
    00297     for (int x = xl; ; x = (x + 1) & HASH_MASK) {
    00298       Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
    00299       for (; v != NULL; v = v->next_new_hash) {
    00300         Vehicle *a = proc(v, data);
    00301         if (find_first && a != NULL) return a;
    00302       }
    00303       if (x == xu) break;
    00304     }
    00305     if (y == yu) break;
    00306   }
    00307
    00308   return NULL;
    00309 }
    00310
    00311
    00323 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
    00324 {
    00325   const int COLL_DIST = 6;
    00326
    00327   /* Hash area to scan is from xl,yl to xu,yu */
    00328   int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
    00329   int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
    00330   int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
    00331   int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
    00332
    00333   return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
    00334 }
    00335
    00350 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
    00351 {
    00352   VehicleFromPosXY(x, y, data, proc, false);
    00353 }
    00354
    00366 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
    00367 {
    00368   return VehicleFromPosXY(x, y, data, proc, true) != NULL;
    00369 }
    00370
    00381 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
    00382 {
    00383   int x = GB(TileX(tile), HASH_RES, HASH_BITS);
    00384   int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
    00385
    00386   Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
    00387   for (; v != NULL; v = v->next_new_hash) {
    00388     if (v->tile != tile) continue;
    00389
    00390     Vehicle *a = proc(v, data);
    00391     if (find_first && a != NULL) return a;
    00392   }
    00393
    00394   return NULL;
    00395 }
    00396
    00410 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
    00411 {
    00412   VehicleFromPos(tile, data, proc, false);
    00413 }
    00414
    00425 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
    00426 {
    00427   return VehicleFromPos(tile, data, proc, true) != NULL;
    00428 }
    00429
    00430
    00431 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
    00432 {
    00433   Vehicle **old_hash = v->old_new_hash;
    00434   Vehicle **new_hash;
    00435
    00436   if (remove) {
    00437     new_hash = NULL;
    00438   } else {
    00439     int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
    00440     int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
    00441     new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
    00442   }
    00443
    00444   if (old_hash == new_hash) return;
    00445
    00446   /* Remove from the old position in the hash table */
    00447   if (old_hash != NULL) {
    00448     if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
    00449     *v->prev_new_hash = v->next_new_hash;
    00450   }
    00451
    00452   /* Insert vehicle at beginning of the new position in the hash table */
    00453   if (new_hash != NULL) {
    00454     v->next_new_hash = *new_hash;
    00455     if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
    00456     v->prev_new_hash = new_hash;
    00457     *new_hash = v;
    00458   }
    00459
    00460   /* Remember current hash position */
    00461   v->old_new_hash = new_hash;
    00462 }
    00463
    00464 static Vehicle *_vehicle_position_hash[0x1000];
    00465
    00466 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
    00467 {
    00468   UpdateNewVehiclePosHash(v, x == INVALID_COORD);
    00469
    00470   Vehicle **old_hash, **new_hash;
    00471   int old_x = v->coord.left;
    00472   int old_y = v->coord.top;
    00473
    00474   new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
    00475   old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
    00476
    00477   if (old_hash == new_hash) return;
    00478
    00479   /* remove from hash table? */
    00480   if (old_hash != NULL) {
    00481     if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
    00482     *v->prev_hash = v->next_hash;
    00483   }
    00484
    00485   /* insert into hash table? */
    00486   if (new_hash != NULL) {
    00487     v->next_hash = *new_hash;
    00488     if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
    00489     v->prev_hash = new_hash;
    00490     *new_hash = v;
    00491   }
    00492 }
    00493
    00494 void ResetVehiclePosHash()
    00495 {
    00496   Vehicle *v;
    00497   FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
    00498   memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
    00499   memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
    00500 }
    00501
    00502 void ResetVehicleColourMap()
    00503 {
    00504   Vehicle *v;
    00505   FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
    00506 }
    00507
    00512 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
    00513 static AutoreplaceMap _vehicles_to_autoreplace;
    00514
    00515 void InitializeVehicles()
    00516 {
    00517   _vehicle_pool.CleanPool();
    00518   _cargo_payment_pool.CleanPool();
    00519
    00520   _age_cargo_skip_counter = 1;
    00521
    00522   _vehicles_to_autoreplace.Reset();
    00523   ResetVehiclePosHash();
    00524 }
    00525
    00526 uint CountVehiclesInChain(const Vehicle *v)
    00527 {
    00528   uint count = 0;
    00529   do count++; while ((v = v->Next()) != NULL);
    00530   return count;
    00531 }
    00532
    00536 bool Vehicle::IsEngineCountable() const
    00537 {
    00538   switch (this->type) {
    00539     case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
    00540     case VEH_TRAIN:
    00541       return !Train::From(this)->IsArticulatedPart() && // tenders and other articulated parts
    00542           !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
    00543     case VEH_ROAD: return RoadVehicle::From(this)->IsRoadVehFront();
    00544     case VEH_SHIP: return true;
    00545     default: return false; // Only count company buildable vehicles
    00546   }
    00547 }
    00548
    00549 void Vehicle::PreDestructor()
    00550 {
    00551   if (CleaningPool()) return;
    00552
    00553   if (Station::IsValidID(this->last_station_visited)) {
    00554     Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
    00555
    00556     HideFillingPercent(&this->fill_percent_te_id);
    00557
    00558     delete this->cargo_payment;
    00559   }
    00560
    00561   if (this->IsEngineCountable()) {
    00562     Company::Get(this->owner)->num_engines[this->engine_type]--;
    00563     if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
    00564
    00565     DeleteGroupHighlightOfVehicle(this);
    00566     if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
    00567     if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
    00568   }
    00569
    00570   if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
    00571     Aircraft *a = Aircraft::From(this);
    00572     Station *st = GetTargetAirportIfValid(a);
    00573     if (st != NULL) {
    00574       const AirportFTA *layout = st->Airport()->layout;
    00575       CLRBITS(st->airport_flags, layout[a->previous_pos].block | layout[a->pos].block);
    00576     }
    00577   }
    00578
    00579
    00580   if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
    00581     RoadVehicle *v = RoadVehicle::From(this);
    00582     if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
    00583       /* Leave the drive through roadstop, when you have not already left it. */
    00584       RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
    00585     }
    00586   }
    00587
    00588   if (this->Previous() == NULL) {
    00589     InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
    00590   }
    00591
    00592   if (this->IsPrimaryVehicle()) {
    00593     DeleteWindowById(WC_VEHICLE_VIEW, this->index);
    00594     DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
    00595     DeleteWindowById(WC_VEHICLE_REFIT, this->index);
    00596     DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
    00597     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
    00598     SetWindowDirty(WC_COMPANY, this->owner);
    00599   }
    00600   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
    00601
    00602   this->cargo.Truncate(0);
    00603   DeleteVehicleOrders(this);
    00604   DeleteDepotHighlightOfVehicle(this);
    00605
    00606   extern void StopGlobalFollowVehicle(const Vehicle *v);
    00607   StopGlobalFollowVehicle(this);
    00608
    00609   ReleaseDisastersTargetingVehicle(this->index);
    00610 }
    00611
    00612 Vehicle::~Vehicle()
    00613 {
    00614   free(this->name);
    00615
    00616   if (CleaningPool()) return;
    00617
    00618   /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
    00619    * it may happen that vehicle chain is deleted when visible */
    00620   if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
    00621
    00622   Vehicle *v = this->Next();
    00623   this->SetNext(NULL);
    00624
    00625   delete v;
    00626
    00627   UpdateVehiclePosHash(this, INVALID_COORD, 0);
    00628   DeleteVehicleNews(this->index, INVALID_STRING_ID);
    00629 }
    00630
    00634 void VehicleEnteredDepotThisTick(Vehicle *v)
    00635 {
    00636   /* Vehicle should stop in the depot if it was in 'stopping' state */
    00637   _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
    00638
    00639   /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
    00640    * stopping in the depot, so we stop it to ensure that it will not reserve
    00641    * the path out of the depot before we might autoreplace it to a different
    00642    * engine. The new engine would not own the reserved path we store that we
    00643    * stopped the vehicle, so autoreplace can start it again */
    00644   v->vehstatus |= VS_STOPPED;
    00645 }
    00646
    00652 static void RunVehicleDayProc()
    00653 {
    00654   if (_game_mode != GM_NORMAL) return;
    00655
    00656   /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
    00657   for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
    00658     Vehicle *v = Vehicle::Get(i);
    00659     if (v == NULL) continue;
    00660
    00661     /* Call the 32-day callback if needed */
    00662     if ((v->day_counter & 0x1F) == 0) {
    00663       uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
    00664       if (callback != CALLBACK_FAILED) {
    00665         if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
    00666         if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
    00667       }
    00668     }
    00669
    00670     /* This is called once per day for each vehicle, but not in the first tick of the day */
    00671     v->OnNewDay();
    00672   }
    00673 }
    00674
    00675 void CallVehicleTicks()
    00676 {
    00677   _vehicles_to_autoreplace.Clear();
    00678
    00679   _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
    00680
    00681   RunVehicleDayProc();
    00682
    00683   Station *st;
    00684   FOR_ALL_STATIONS(st) LoadUnloadStation(st);
    00685
    00686   Vehicle *v;
    00687   FOR_ALL_VEHICLES(v) {
    00688     /* Vehicle could be deleted in this tick */
    00689     if (!v->Tick()) {
    00690       assert(Vehicle::Get(vehicle_index) == NULL);
    00691       continue;
    00692     }
    00693
    00694     assert(Vehicle::Get(vehicle_index) == v);
    00695
    00696     switch (v->type) {
    00697       default: break;
    00698
    00699       case VEH_TRAIN:
    00700       case VEH_ROAD:
    00701       case VEH_AIRCRAFT:
    00702       case VEH_SHIP:
    00703         if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
    00704
    00705         if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
    00706         if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
    00707         if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsRoadVehFront()) continue;
    00708
    00709         v->motion_counter += v->cur_speed;
    00710         /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
    00711         if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
    00712
    00713         /* Play an alterate running sound every 16 ticks */
    00714         if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
    00715     }
    00716   }
    00717
    00718   for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
    00719     v = it->first;
    00720     /* Autoreplace needs the current company set as the vehicle owner */
    00721     _current_company = v->owner;
    00722
    00723     /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
    00724      * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
    00725      * they are already leaving the depot again before being replaced. */
    00726     if (it->second) v->vehstatus &= ~VS_STOPPED;
    00727
    00728     /* Store the position of the effect as the vehicle pointer will become invalid later */
    00729     int x = v->x_pos;
    00730     int y = v->y_pos;
    00731     int z = v->z_pos;
    00732
    00733     const Company *c = Company::Get(_current_company);
    00734     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
    00735     CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
    00736     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
    00737
    00738     if (!IsLocalCompany()) continue;
    00739
    00740     if (res.Succeeded()) {
    00741       ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
    00742       continue;
    00743     }
    00744
    00745     StringID error_message = res.GetErrorMessage();
    00746     if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
    00747
    00748     if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
    00749
    00750     StringID message;
    00751     if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
    00752       message = error_message;
    00753     } else {
    00754       message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
    00755     }
    00756
    00757     SetDParam(0, v->index);
    00758     SetDParam(1, error_message);
    00759     AddVehicleNewsItem(message, NS_ADVICE, v->index);
    00760   }
    00761
    00762   _current_company = OWNER_NONE;
    00763 }
    00764
    00765 static void DoDrawVehicle(const Vehicle *v)
    00766 {
    00767   SpriteID image = v->cur_image;
    00768   SpriteID pal = PAL_NONE;
    00769
    00770   if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
    00771
    00772   AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
    00773     v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
    00774 }
    00775
    00776 void ViewportAddVehicles(DrawPixelInfo *dpi)
    00777 {
    00778   /* The bounding rectangle */
    00779   const int l = dpi->left;
    00780   const int r = dpi->left + dpi->width;
    00781   const int t = dpi->top;
    00782   const int b = dpi->top + dpi->height;
    00783
    00784   /* The hash area to scan */
    00785   int xl, xu, yl, yu;
    00786
    00787   if (dpi->width + 70 < (1 << (7 + 6))) {
    00788     xl = GB(l - 70, 7, 6);
    00789     xu = GB(r,      7, 6);
    00790   } else {
    00791     /* scan whole hash row */
    00792     xl = 0;
    00793     xu = 0x3F;
    00794   }
    00795
    00796   if (dpi->height + 70 < (1 << (6 + 6))) {
    00797     yl = GB(t - 70, 6, 6) << 6;
    00798     yu = GB(b,      6, 6) << 6;
    00799   } else {
    00800     /* scan whole column */
    00801     yl = 0;
    00802     yu = 0x3F << 6;
    00803   }
    00804
    00805   for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
    00806     for (int x = xl;; x = (x + 1) & 0x3F) {
    00807       const Vehicle *v = _vehicle_position_hash[x + y]; // already masked & 0xFFF
    00808
    00809       while (v != NULL) {
    00810         if (!(v->vehstatus & VS_HIDDEN) &&
    00811             l <= v->coord.right &&
    00812             t <= v->coord.bottom &&
    00813             r >= v->coord.left &&
    00814             b >= v->coord.top) {
    00815           DoDrawVehicle(v);
    00816         }
    00817         v = v->next_hash;
    00818       }
    00819
    00820       if (x == xu) break;
    00821     }
    00822
    00823     if (y == yu) break;
    00824   }
    00825 }
    00826
    00827 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
    00828 {
    00829   Vehicle *found = NULL, *v;
    00830   uint dist, best_dist = UINT_MAX;
    00831
    00832   if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
    00833
    00834   x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
    00835   y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
    00836
    00837   FOR_ALL_VEHICLES(v) {
    00838     if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
    00839         x >= v->coord.left && x <= v->coord.right &&
    00840         y >= v->coord.top && y <= v->coord.bottom) {
    00841
    00842       dist = max(
    00843         abs(((v->coord.left + v->coord.right) >> 1) - x),
    00844         abs(((v->coord.top + v->coord.bottom) >> 1) - y)
    00845       );
    00846
    00847       if (dist < best_dist) {
    00848         found = v;
    00849         best_dist = dist;
    00850       }
    00851     }
    00852   }
    00853
    00854   return found;
    00855 }
    00856
    00857 void DecreaseVehicleValue(Vehicle *v)
    00858 {
    00859   v->value -= v->value >> 8;
    00860   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    00861 }
    00862
    00863 static const byte _breakdown_chance[64] = {
    00864     3,   3,   3,   3,   3,   3,   3,   3,
    00865     4,   4,   5,   5,   6,   6,   7,   7,
    00866     8,   8,   9,   9,  10,  10,  11,  11,
    00867    12,  13,  13,  13,  13,  14,  15,  16,
    00868    17,  19,  21,  25,  28,  31,  34,  37,
    00869    40,  44,  48,  52,  56,  60,  64,  68,
    00870    72,  80,  90, 100, 110, 120, 130, 140,
    00871   150, 170, 190, 210, 230, 250, 250, 250,
    00872 };
    00873
    00874 void CheckVehicleBreakdown(Vehicle *v)
    00875 {
    00876   int rel, rel_old;
    00877
    00878   /* decrease reliability */
    00879   v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
    00880   if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    00881
    00882   if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
    00883       _settings_game.difficulty.vehicle_breakdowns < 1 ||
    00884       v->cur_speed < 5 || _game_mode == GM_MENU) {
    00885     return;
    00886   }
    00887
    00888   uint32 r = Random();
    00889
    00890   /* increase chance of failure */
    00891   int chance = v->breakdown_chance + 1;
    00892   if (Chance16I(1, 25, r)) chance += 25;
    00893   v->breakdown_chance = min(255, chance);
    00894
    00895   /* calculate reliability value to use in comparison */
    00896   rel = v->reliability;
    00897   if (v->type == VEH_SHIP) rel += 0x6666;
    00898
    00899   /* reduced breakdowns? */
    00900   if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
    00901
    00902   /* check if to break down */
    00903   if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
    00904     v->breakdown_ctr    = GB(r, 16, 6) + 0x3F;
    00905     v->breakdown_delay  = GB(r, 24, 7) + 0x80;
    00906     v->breakdown_chance = 0;
    00907   }
    00908 }
    00909
    00910 void AgeVehicle(Vehicle *v)
    00911 {
    00912   if (v->age < 65535) v->age++;
    00913
    00914   int age = v->age - v->max_age;
    00915   if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
    00916       age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
    00917     v->reliability_spd_dec <<= 1;
    00918   }
    00919
    00920   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    00921
    00922   /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
    00923   if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
    00924
    00925   /* Don't warn if a renew is active */
    00926   if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
    00927
    00928   StringID str;
    00929   if (age == -DAYS_IN_LEAP_YEAR) {
    00930     str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
    00931   } else if (age == 0) {
    00932     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
    00933   } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
    00934     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
    00935   } else {
    00936     return;
    00937   }
    00938
    00939   SetDParam(0, v->index);
    00940   AddVehicleNewsItem(str, NS_ADVICE, v->index);
    00941 }
    00942
    00949 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
    00950 {
    00951   int count = 0;
    00952   int max = 0;
    00953   int cars = 0;
    00954   int unloading = 0;
    00955   bool loading = false;
    00956
    00957   const Vehicle *u = v;
    00958   const Station *st = v->last_station_visited != INVALID_STATION ? Station::Get(v->last_station_visited) : NULL;
    00959
    00960   /* Count up max and used */
    00961   for (; v != NULL; v = v->Next()) {
    00962     count += v->cargo.Count();
    00963     max += v->cargo_cap;
    00964     if (v->cargo_cap != 0 && colour != NULL) {
    00965       unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
    00966       loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
    00967       cars++;
    00968     }
    00969   }
    00970
    00971   if (colour != NULL) {
    00972     if (unloading == 0 && loading) {
    00973       *colour = STR_PERCENT_UP;
    00974     } else if (cars == unloading || !loading) {
    00975       *colour = STR_PERCENT_DOWN;
    00976     } else {
    00977       *colour = STR_PERCENT_UP_DOWN;
    00978     }
    00979   }
    00980
    00981   /* Train without capacity */
    00982   if (max == 0) return 100;
    00983
    00984   /* Return the percentage */
    00985   return (count * 100) / max;
    00986 }
    00987
    00988 void VehicleEnterDepot(Vehicle *v)
    00989 {
    00990   /* Always work with the front of the vehicle */
    00991   assert(v == v->First());
    00992
    00993   switch (v->type) {
    00994     case VEH_TRAIN: {
    00995       Train *t = Train::From(v);
    00996       SetWindowClassesDirty(WC_TRAINS_LIST);
    00997       /* Clear path reservation */
    00998       SetDepotReservation(t->tile, false);
    00999       if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
    01000
    01001       UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
    01002       t->time_counter = 0;
    01003       ClrBit(t->flags, VRF_TOGGLE_REVERSE);
    01004       TrainConsistChanged(t, true);
    01005       break;
    01006     }
    01007
    01008     case VEH_ROAD:
    01009       SetWindowClassesDirty(WC_ROADVEH_LIST);
    01010       break;
    01011
    01012     case VEH_SHIP:
    01013       SetWindowClassesDirty(WC_SHIPS_LIST);
    01014       Ship::From(v)->state = TRACK_BIT_DEPOT;
    01015       RecalcShipStuff(v);
    01016       break;
    01017
    01018     case VEH_AIRCRAFT:
    01019       SetWindowClassesDirty(WC_AIRCRAFT_LIST);
    01020       HandleAircraftEnterHangar(Aircraft::From(v));
    01021       break;
    01022     default: NOT_REACHED();
    01023   }
    01024
    01025   if (v->type != VEH_TRAIN) {
    01026     /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
    01027      * 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 */
    01028     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    01029   }
    01030   SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
    01031
    01032   v->vehstatus |= VS_HIDDEN;
    01033   v->cur_speed = 0;
    01034
    01035   VehicleServiceInDepot(v);
    01036
    01037   TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
    01038
    01039   if (v->current_order.IsType(OT_GOTO_DEPOT)) {
    01040     SetWindowDirty(WC_VEHICLE_VIEW, v->index);
    01041
    01042     const Order *real_order = v->GetOrder(v->cur_order_index);
    01043     Order t = v->current_order;
    01044     v->current_order.MakeDummy();
    01045
    01046     /* Test whether we are heading for this depot. If not, do nothing.
    01047      * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
    01048     if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
    01049         real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
    01050         (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
    01051       /* We are heading for another depot, keep driving. */
    01052       return;
    01053     }
    01054
    01055     if (t.IsRefit()) {
    01056       _current_company = v->owner;
    01057       CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
    01058
    01059       if (CmdFailed(cost)) {
    01060         _vehicles_to_autoreplace[v] = false;
    01061         if (v->owner == _local_company) {
    01062           /* Notify the user that we stopped the vehicle */
    01063           SetDParam(0, v->index);
    01064           AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
    01065         }
    01066       } else if (v->owner == _local_company && cost.GetCost() != 0) {
    01067         ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
    01068       }
    01069     }
    01070
    01071     if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
    01072       /* Part of orders */
    01073       UpdateVehicleTimetable(v, true);
    01074       v->IncrementOrderIndex();
    01075     }
    01076     if (t.GetDepotActionType() & ODATFB_HALT) {
    01077       /* Vehicles are always stopped on entering depots. Do not restart this one. */
    01078       _vehicles_to_autoreplace[v] = false;
    01079       if (v->owner == _local_company) {
    01080         SetDParam(0, v->index);
    01081         AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
    01082       }
    01083       AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
    01084     }
    01085   }
    01086 }
    01087
    01088
    01096 void VehicleMove(Vehicle *v, bool update_viewport)
    01097 {
    01098   int img = v->cur_image;
    01099   Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
    01100   const Sprite *spr = GetSprite(img, ST_NORMAL);
    01101
    01102   pt.x += spr->x_offs;
    01103   pt.y += spr->y_offs;
    01104
    01105   UpdateVehiclePosHash(v, pt.x, pt.y);
    01106
    01107   Rect old_coord = v->coord;
    01108   v->coord.left   = pt.x;
    01109   v->coord.top    = pt.y;
    01110   v->coord.right  = pt.x + spr->width + 2;
    01111   v->coord.bottom = pt.y + spr->height + 2;
    01112
    01113   if (update_viewport) {
    01114     MarkAllViewportsDirty(
    01115       min(old_coord.left,   v->coord.left),
    01116       min(old_coord.top,    v->coord.top),
    01117       max(old_coord.right,  v->coord.right) + 1,
    01118       max(old_coord.bottom, v->coord.bottom) + 1
    01119     );
    01120   }
    01121 }
    01122
    01131 void MarkSingleVehicleDirty(const Vehicle *v)
    01132 {
    01133   MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
    01134 }
    01135
    01140 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
    01141 {
    01142   static const int8 _delta_coord[16] = {
    01143     -1,-1,-1, 0, 1, 1, 1, 0, /* x */
    01144     -1, 0, 1, 1, 1, 0,-1,-1, /* y */
    01145   };
    01146
    01147   int x = v->x_pos + _delta_coord[v->direction];
    01148   int y = v->y_pos + _delta_coord[v->direction + 8];
    01149
    01150   GetNewVehiclePosResult gp;
    01151   gp.x = x;
    01152   gp.y = y;
    01153   gp.old_tile = v->tile;
    01154   gp.new_tile = TileVirtXY(x, y);
    01155   return gp;
    01156 }
    01157
    01158 static const Direction _new_direction_table[] = {
    01159   DIR_N,  DIR_NW, DIR_W,
    01160   DIR_NE, DIR_SE, DIR_SW,
    01161   DIR_E,  DIR_SE, DIR_S
    01162 };
    01163
    01164 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
    01165 {
    01166   int i = 0;
    01167
    01168   if (y >= v->y_pos) {
    01169     if (y != v->y_pos) i += 3;
    01170     i += 3;
    01171   }
    01172
    01173   if (x >= v->x_pos) {
    01174     if (x != v->x_pos) i++;
    01175     i++;
    01176   }
    01177
    01178   Direction dir = v->direction;
    01179
    01180   DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
    01181   if (dirdiff == DIRDIFF_SAME) return dir;
    01182   return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
    01183 }
    01184
    01194 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
    01195 {
    01196   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
    01197 }
    01198
    01199 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
    01200 {
    01201   /* Find maximum */
    01202   const Vehicle *v;
    01203   FOR_ALL_VEHICLES(v) {
    01204     if (v->type == type && v->owner == owner) {
    01205       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
    01206     }
    01207   }
    01208
    01209   if (this->maxid == 0) return;
    01210
    01211   this->maxid++; // so there is space for last item (with v->unitnumber == maxid)
    01212   this->maxid++; // this one will always be free (well, it will fail when there are 65535 units, so this overflows)
    01213
    01214   this->cache = CallocT<bool>(this->maxid);
    01215
    01216   /* Fill the cache */
    01217   FOR_ALL_VEHICLES(v) {
    01218     if (v->type == type && v->owner == owner) {
    01219       this->cache[v->unitnumber] = true;
    01220     }
    01221   }
    01222 }
    01223
    01224 UnitID FreeUnitIDGenerator::NextID()
    01225 {
    01226   if (this->maxid <= this->curid) return ++this->curid;
    01227
    01228   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
    01229
    01230   return this->curid;
    01231 }
    01232
    01233 UnitID GetFreeUnitNumber(VehicleType type)
    01234 {
    01235   FreeUnitIDGenerator gen(type, _current_company);
    01236
    01237   return gen.NextID();
    01238 }
    01239
    01240
    01249 bool CanBuildVehicleInfrastructure(VehicleType type)
    01250 {
    01251   assert(IsCompanyBuildableVehicleType(type));
    01252
    01253   if (!Company::IsValidID(_local_company)) return false;
    01254   if (_settings_client.gui.always_build_infrastructure) return true;
    01255
    01256   UnitID max;
    01257   switch (type) {
    01258     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
    01259     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
    01260     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
    01261     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
    01262     default: NOT_REACHED();
    01263   }
    01264
    01265   /* We can build vehicle infrastructure when we may build the vehicle type */
    01266   if (max > 0) {
    01267     /* Can we actually build the vehicle type? */
    01268     const Engine *e;
    01269     FOR_ALL_ENGINES_OF_TYPE(e, type) {
    01270       if (HasBit(e->company_avail, _local_company)) return true;
    01271     }
    01272     return false;
    01273   }
    01274
    01275   /* We should be able to build infrastructure when we have the actual vehicle type */
    01276   const Vehicle *v;
    01277   FOR_ALL_VEHICLES(v) {
    01278     if (v->owner == _local_company && v->type == type) return true;
    01279   }
    01280
    01281   return false;
    01282 }
    01283
    01284
    01293 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
    01294 {
    01295   const Company *c = Company::Get(company);
    01296   LiveryScheme scheme = LS_DEFAULT;
    01297   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
    01298
    01299   /* The default livery is always available for use, but its in_use flag determines
    01300    * whether any _other_ liveries are in use. */
    01301   if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
    01302     /* Determine the livery scheme to use */
    01303     const Engine *e = Engine::Get(engine_type);
    01304     switch (e->type) {
    01305       default: NOT_REACHED();
    01306       case VEH_TRAIN: {
    01307         if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (Train::From(v)->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
    01308           /* Wagonoverrides use the coloir scheme of the front engine.
    01309            * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
    01310           engine_type = parent_engine_type;
    01311           e = Engine::Get(engine_type);
    01312           /* Note: Luckily cargo_type is not needed for engines */
    01313         }
    01314
    01315         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01316         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01317         if (e->u.rail.railveh_type == RAILVEH_WAGON) {
    01318           if (!CargoSpec::Get(cargo_type)->is_freight) {
    01319             if (parent_engine_type == INVALID_ENGINE) {
    01320               scheme = LS_PASSENGER_WAGON_STEAM;
    01321             } else {
    01322               switch (RailVehInfo(parent_engine_type)->engclass) {
    01323                 default: NOT_REACHED();
    01324                 case EC_STEAM:    scheme = LS_PASSENGER_WAGON_STEAM;    break;
    01325                 case EC_DIESEL:   scheme = LS_PASSENGER_WAGON_DIESEL;   break;
    01326                 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
    01327                 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
    01328                 case EC_MAGLEV:   scheme = LS_PASSENGER_WAGON_MAGLEV;   break;
    01329               }
    01330             }
    01331           } else {
    01332             scheme = LS_FREIGHT_WAGON;
    01333           }
    01334         } else {
    01335           bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
    01336
    01337           switch (e->u.rail.engclass) {
    01338             default: NOT_REACHED();
    01339             case EC_STEAM:    scheme = LS_STEAM; break;
    01340             case EC_DIESEL:   scheme = is_mu ? LS_DMU : LS_DIESEL;   break;
    01341             case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
    01342             case EC_MONORAIL: scheme = LS_MONORAIL; break;
    01343             case EC_MAGLEV:   scheme = LS_MAGLEV; break;
    01344           }
    01345         }
    01346         break;
    01347       }
    01348
    01349       case VEH_ROAD: {
    01350         /* Always use the livery of the front */
    01351         if (v != NULL && parent_engine_type != INVALID_ENGINE) {
    01352           engine_type = parent_engine_type;
    01353           e = Engine::Get(engine_type);
    01354           cargo_type = v->First()->cargo_type;
    01355         }
    01356         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01357         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01358
    01359         /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
    01360         if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
    01361           /* Tram */
    01362           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
    01363         } else {
    01364           /* Bus or truck */
    01365           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
    01366         }
    01367         break;
    01368       }
    01369
    01370       case VEH_SHIP: {
    01371         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01372         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01373         scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
    01374         break;
    01375       }
    01376
    01377       case VEH_AIRCRAFT: {
    01378         switch (e->u.air.subtype) {
    01379           case AIR_HELI: scheme = LS_HELICOPTER; break;
    01380           case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
    01381           case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
    01382         }
    01383         break;
    01384       }
    01385     }
    01386
    01387     /* Switch back to the default scheme if the resolved scheme is not in use */
    01388     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
    01389   }
    01390
    01391   return &c->livery[scheme];
    01392 }
    01393
    01394
    01395 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
    01396 {
    01397   SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
    01398
    01399   /* Return cached value if any */
    01400   if (map != PAL_NONE) return map;
    01401
    01402   const Engine *e = Engine::Get(engine_type);
    01403
    01404   /* Check if we should use the colour map callback */
    01405   if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
    01406     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
    01407     /* A return value of 0xC000 is stated to "use the default two-colour
    01408      * maps" which happens to be the failure action too... */
    01409     if (callback != CALLBACK_FAILED && callback != 0xC000) {
    01410       map = GB(callback, 0, 14);
    01411       /* If bit 14 is set, then the company colours are applied to the
    01412        * map else it's returned as-is. */
    01413       if (!HasBit(callback, 14)) {
    01414         /* Update cache */
    01415         if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
    01416         return map;
    01417       }
    01418     }
    01419   }
    01420
    01421   bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
    01422
    01423   if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
    01424
    01425   /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
    01426   if (!Company::IsValidID(company)) return map;
    01427
    01428   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
    01429
    01430   map += livery->colour1;
    01431   if (twocc) map += livery->colour2 * 16;
    01432
    01433   /* Update cache */
    01434   if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
    01435   return map;
    01436 }
    01437
    01438 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
    01439 {
    01440   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
    01441 }
    01442
    01443 SpriteID GetVehiclePalette(const Vehicle *v)
    01444 {
    01445   if (v->type == VEH_TRAIN) {
    01446     return GetEngineColourMap(v->engine_type, v->owner, Train::From(v)->tcache.first_engine, v);
    01447   } else if (v->type == VEH_ROAD) {
    01448     return GetEngineColourMap(v->engine_type, v->owner, RoadVehicle::From(v)->rcache.first_engine, v);
    01449   }
    01450
    01451   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
    01452 }
    01453
    01462 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
    01463 {
    01464   if (mail_capacity != NULL) *mail_capacity = 0;
    01465   const Engine *e = Engine::Get(v->engine_type);
    01466
    01467   if (!e->CanCarryCargo()) return 0;
    01468
    01469   if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
    01470     *mail_capacity = e->u.air.mail_capacity;
    01471   }
    01472   CargoID default_cargo = e->GetDefaultCargoType();
    01473
    01474   /* Check the refit capacity callback if we are not in the default configuration.
    01475    * Note: This might change to become more consistent/flexible/sane, esp. when default cargo is first refittable. */
    01476   if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
    01477       (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
    01478     uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
    01479     if (callback != CALLBACK_FAILED) return callback;
    01480   }
    01481
    01482   /* Get capacity according to property resp. CB */
    01483   uint capacity;
    01484   switch (e->type) {
    01485     case VEH_TRAIN:    capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY,   e->u.rail.capacity); break;
    01486     case VEH_ROAD:     capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
    01487     case VEH_SHIP:     capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY,    e->u.ship.capacity); break;
    01488     case VEH_AIRCRAFT: capacity = e->u.air.passenger_capacity; break;
    01489     default: NOT_REACHED();
    01490   }
    01491
    01492   /* Apply multipliers depending on cargo- and vehicletype.
    01493    * Note: This might change to become more consistent/flexible. */
    01494   if (e->type != VEH_SHIP) {
    01495     if (e->type == VEH_AIRCRAFT) {
    01496       if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
    01497         capacity += e->u.air.mail_capacity;
    01498       }
    01499       if (v->cargo_type == CT_MAIL) return capacity;
    01500     } else {
    01501       switch (default_cargo) {
    01502         case CT_PASSENGERS: break;
    01503         case CT_MAIL:
    01504         case CT_GOODS: capacity *= 2; break;
    01505         default:       capacity *= 4; break;
    01506       }
    01507     }
    01508     switch (v->cargo_type) {
    01509       case CT_PASSENGERS: break;
    01510       case CT_MAIL:
    01511       case CT_GOODS: capacity /= 2; break;
    01512       default:       capacity /= 4; break;
    01513     }
    01514   }
    01515
    01516   return capacity;
    01517 }
    01518
    01519
    01520 void Vehicle::BeginLoading()
    01521 {
    01522   assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
    01523
    01524   if (this->current_order.IsType(OT_GOTO_STATION) &&
    01525       this->current_order.GetDestination() == this->last_station_visited) {
    01526     current_order.MakeLoading(true);
    01527     UpdateVehicleTimetable(this, true);
    01528
    01529     /* Furthermore add the Non Stop flag to mark that this station
    01530      * is the actual destination of the vehicle, which is (for example)
    01531      * necessary to be known for HandleTrainLoading to determine
    01532      * whether the train is lost or not; not marking a train lost
    01533      * that arrives at random stations is bad. */
    01534     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
    01535
    01536   } else {
    01537     current_order.MakeLoading(false);
    01538   }
    01539
    01540   Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
    01541
    01542   PrepareUnload(this);
    01543
    01544   SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
    01545   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01546   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
    01547   SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
    01548
    01549   Station::Get(this->last_station_visited)->MarkTilesDirty(true);
    01550   this->cur_speed = 0;
    01551   this->MarkDirty();
    01552 }
    01553
    01554 void Vehicle::LeaveStation()
    01555 {
    01556   assert(current_order.IsType(OT_LOADING));
    01557
    01558   delete this->cargo_payment;
    01559
    01560   /* Only update the timetable if the vehicle was supposed to stop here. */
    01561   if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
    01562
    01563   current_order.MakeLeaveStation();
    01564   Station *st = Station::Get(this->last_station_visited);
    01565   st->loading_vehicles.remove(this);
    01566
    01567   HideFillingPercent(&this->fill_percent_te_id);
    01568
    01569   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
    01570     /* Trigger station animation (trains only) */
    01571     if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
    01572
    01573     /* Try to reserve a path when leaving the station as we
    01574      * might not be marked as wanting a reservation, e.g.
    01575      * when an overlength train gets turned around in a station. */
    01576     if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(this->GetVehicleTrackdir()), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
    01577       TryPathReserve(Train::From(this), true, true);
    01578     }
    01579   }
    01580 }
    01581
    01582
    01583 void Vehicle::HandleLoading(bool mode)
    01584 {
    01585   switch (this->current_order.GetType()) {
    01586     case OT_LOADING: {
    01587       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
    01588
    01589       /* Pay the loading fee for using someone else's station, if appropriate */
    01590       if (!mode && this->type != VEH_TRAIN) PayStationSharingFee(this, Station::Get(this->last_station_visited));
    01591
    01592       /* Not the first call for this tick, or still loading */
    01593       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
    01594           (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
    01595
    01596       this->PlayLeaveStationSound();
    01597
    01598       bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
    01599       this->LeaveStation();
    01600
    01601       /* If this was not the final order, don't remove it from the list. */
    01602       if (!at_destination_station) return;
    01603       break;
    01604     }
    01605
    01606     case OT_DUMMY: break;
    01607
    01608     default: return;
    01609   }
    01610
    01611   this->IncrementOrderIndex();
    01612 }
    01613
    01614 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
    01615 {
    01616   if (!CheckOwnership(this->owner)) return CMD_ERROR;
    01617   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
    01618   if (this->IsStoppedInDepot()) return CMD_ERROR;
    01619
    01620   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
    01621     bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
    01622     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
    01623       /* We called with a different DEPOT_SERVICE setting.
    01624        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
    01625        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
    01626       if (flags & DC_EXEC) {
    01627         this->current_order.SetDepotOrderType(ODTF_MANUAL);
    01628         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
    01629         SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01630       }
    01631       return CommandCost();
    01632     }
    01633
    01634     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
    01635     if (flags & DC_EXEC) {
    01636       /* If the orders to 'goto depot' are in the orders list (forced servicing),
    01637        * then skip to the next order; effectively cancelling this forced service */
    01638       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
    01639
    01640       this->current_order.MakeDummy();
    01641       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01642     }
    01643     return CommandCost();
    01644   }
    01645
    01646   TileIndex location;
    01647   DestinationID destination;
    01648   bool reverse;
    01649   static const StringID no_depot[] = {STR_ERROR_UNABLE_TO_FIND_ROUTE_TO, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_CAN_T_SEND_AIRCRAFT_TO_HANGAR};
    01650   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
    01651
    01652   if (flags & DC_EXEC) {
    01653     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
    01654
    01655     this->dest_tile = location;
    01656     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
    01657     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
    01658     SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    01659
    01660     /* If there is no depot in front, reverse automatically (trains only) */
    01661     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
    01662
    01663     if (this->type == VEH_AIRCRAFT) {
    01664       Aircraft *a = Aircraft::From(this);
    01665       if (a->state == FLYING && a->targetairport != destination) {
    01666         /* The aircraft is now heading for a different hangar than the next in the orders */
    01667         extern void AircraftNextAirportPos_and_Order(Aircraft *a);
    01668         AircraftNextAirportPos_and_Order(a);
    01669       }
    01670     }
    01671   }
    01672
    01673   return CommandCost();
    01674
    01675 }
    01676
    01677 void Vehicle::SetNext(Vehicle *next)
    01678 {
    01679   assert(this != next);
    01680
    01681   if (this->next != NULL) {
    01682     /* We had an old next vehicle. Update the first and previous pointers */
    01683     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    01684       v->first = this->next;
    01685     }
    01686     this->next->previous = NULL;
    01687   }
    01688
    01689   this->next = next;
    01690
    01691   if (this->next != NULL) {
    01692     /* A new next vehicle. Update the first and previous pointers */
    01693     if (this->next->previous != NULL) this->next->previous->next = NULL;
    01694     this->next->previous = this;
    01695     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    01696       v->first = this->first;
    01697     }
    01698   }
    01699 }
    01700
    01701 void Vehicle::AddToShared(Vehicle *shared_chain)
    01702 {
    01703   assert(this->previous_shared == NULL && this->next_shared == NULL);
    01704
    01705   if (!shared_chain->orders.list) {
    01706     assert(shared_chain->previous_shared == NULL);
    01707     assert(shared_chain->next_shared == NULL);
    01708     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
    01709   }
    01710
    01711   this->next_shared     = shared_chain->next_shared;
    01712   this->previous_shared = shared_chain;
    01713
    01714   shared_chain->next_shared = this;
    01715
    01716   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
    01717
    01718   shared_chain->orders.list->AddVehicle(this);
    01719 }
    01720
    01721 void Vehicle::RemoveFromShared()
    01722 {
    01723   /* Remember if we were first and the old window number before RemoveVehicle()
    01724    * as this changes first if needed. */
    01725   bool were_first = (this->FirstShared() == this);
    01726   uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
    01727
    01728   this->orders.list->RemoveVehicle(this);
    01729
    01730   if (!were_first) {
    01731     /* We are not the first shared one, so only relink our previous one. */
    01732     this->previous_shared->next_shared = this->NextShared();
    01733   }
    01734
    01735   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
    01736
    01737
    01738   if (this->orders.list->GetNumVehicles() == 1) {
    01739     /* When there is only one vehicle, remove the shared order list window. */
    01740     DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
    01741     InvalidateVehicleOrder(this->FirstShared(), 0);
    01742   } else if (were_first) {
    01743     /* If we were the first one, update to the new first one.
    01744      * Note: FirstShared() is already the new first */
    01745     InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
    01746   }
    01747
    01748   this->next_shared     = NULL;
    01749   this->previous_shared = NULL;
    01750 }
    01751
    01752 void StopAllVehicles()
    01753 {
    01754   Vehicle *v;
    01755   FOR_ALL_VEHICLES(v) {
    01756     /* Code ripped from CmdStartStopTrain. Can't call it, because of
    01757      * ownership problems, so we'll duplicate some code, for now */
    01758     v->vehstatus |= VS_STOPPED;
    01759     v->MarkDirty();
    01760     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    01761     SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
    01762   }
    01763 }
    01764
    01765 void VehiclesYearlyLoop()
    01766 {
    01767   Vehicle *v;
    01768   FOR_ALL_VEHICLES(v) {
    01769     if (v->IsPrimaryVehicle()) {
    01770       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
    01771       Money profit = v->GetDisplayProfitThisYear();
    01772       if (v->age >= 730 && profit < 0) {
    01773         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
    01774           SetDParam(0, v->index);
    01775           SetDParam(1, profit);
    01776           AddVehicleNewsItem(
    01777             STR_NEWS_VEHICLE_IS_UNPROFITABLE,
    01778             NS_ADVICE,
    01779             v->index
    01780           );
    01781         }
    01782         AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
    01783       }
    01784
    01785       v->profit_last_year = v->profit_this_year;
    01786       v->profit_this_year = 0;
    01787       SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    01788     }
    01789   }
    01790 }
    01791
    01792
    01802 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
    01803 {
    01804   const Engine *e = Engine::GetIfValid(engine_type);
    01805   assert(e != NULL);
    01806
    01807   switch (e->type) {
    01808     case VEH_TRAIN:
    01809       return (st->facilities & FACIL_TRAIN) != 0;
    01810
    01811     case VEH_ROAD:
    01812       /* For road vehicles we need the vehicle to know whether it can actually
    01813        * use the station, but if it doesn't have facilities for RVs it is
    01814        * certainly not possible that the station can be used. */
    01815       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
    01816
    01817     case VEH_SHIP:
    01818       return (st->facilities & FACIL_DOCK) != 0;
    01819
    01820     case VEH_AIRCRAFT:
    01821       return (st->facilities & FACIL_AIRPORT) != 0 &&
    01822           (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
    01823
    01824     default:
    01825       return false;
    01826   }
    01827 }
    01828
    01835 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
    01836 {
    01837   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
    01838
    01839   return CanVehicleUseStation(v->engine_type, st);
    01840 }