# vehicle.cpp

Go to the documentation of this file.

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