# vehicle.cpp

Go to the documentation of this file.

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