vehicle.cpp

Go to the documentation of this file.
00001 /* $Id: vehicle.cpp 20608 2010-08-24 00:03:26Z 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 
00539 void CountCompanyVehicles(CompanyID cid, uint counts[4])
00540 {
00541   for (uint i = 0; i < 4; i++) counts[i] = 0;
00542 
00543   const Vehicle *v;
00544   FOR_ALL_VEHICLES(v) {
00545     if (v->owner == cid && v->IsPrimaryVehicle()) counts[v->type]++;
00546   }
00547 }
00548 
00552 bool Vehicle::IsEngineCountable() const
00553 {
00554   switch (this->type) {
00555     case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
00556     case VEH_TRAIN:
00557       return !Train::From(this)->IsArticulatedPart() && // tenders and other articulated parts
00558           !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
00559     case VEH_ROAD: return RoadVehicle::From(this)->IsRoadVehFront();
00560     case VEH_SHIP: return true;
00561     default: return false; // Only count company buildable vehicles
00562   }
00563 }
00564 
00565 void Vehicle::PreDestructor()
00566 {
00567   if (CleaningPool()) return;
00568 
00569   if (Station::IsValidID(this->last_station_visited)) {
00570     Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00571 
00572     HideFillingPercent(&this->fill_percent_te_id);
00573 
00574     delete this->cargo_payment;
00575   }
00576 
00577   if (this->IsEngineCountable()) {
00578     Company::Get(this->owner)->num_engines[this->engine_type]--;
00579     if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00580 
00581     DeleteGroupHighlightOfVehicle(this);
00582     if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
00583     if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00584   }
00585 
00586   if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00587     Aircraft *a = Aircraft::From(this);
00588     Station *st = GetTargetAirportIfValid(a);
00589     if (st != NULL) {
00590       const AirportFTA *layout = st->Airport()->layout;
00591       CLRBITS(st->airport_flags, layout[a->previous_pos].block | layout[a->pos].block);
00592     }
00593   }
00594 
00595 
00596   if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00597     RoadVehicle *v = RoadVehicle::From(this);
00598     if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00599       /* Leave the drive through roadstop, when you have not already left it. */
00600       RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00601     }
00602   }
00603 
00604   if (this->Previous() == NULL) {
00605     InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00606   }
00607 
00608   if (this->IsPrimaryVehicle()) {
00609     DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00610     DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00611     DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00612     DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00613     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00614     SetWindowDirty(WC_COMPANY, this->owner);
00615   }
00616   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00617 
00618   this->cargo.Truncate(0);
00619   DeleteVehicleOrders(this);
00620   DeleteDepotHighlightOfVehicle(this);
00621 
00622   extern void StopGlobalFollowVehicle(const Vehicle *v);
00623   StopGlobalFollowVehicle(this);
00624 
00625   ReleaseDisastersTargetingVehicle(this->index);
00626 }
00627 
00628 Vehicle::~Vehicle()
00629 {
00630   free(this->name);
00631 
00632   if (CleaningPool()) return;
00633 
00634   /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
00635    * it may happen that vehicle chain is deleted when visible */
00636   if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00637 
00638   Vehicle *v = this->Next();
00639   this->SetNext(NULL);
00640 
00641   delete v;
00642 
00643   UpdateVehiclePosHash(this, INVALID_COORD, 0);
00644   DeleteVehicleNews(this->index, INVALID_STRING_ID);
00645 }
00646 
00650 void VehicleEnteredDepotThisTick(Vehicle *v)
00651 {
00652   /* Vehicle should stop in the depot if it was in 'stopping' state */
00653   _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00654 
00655   /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
00656    * stopping in the depot, so we stop it to ensure that it will not reserve
00657    * the path out of the depot before we might autoreplace it to a different
00658    * engine. The new engine would not own the reserved path we store that we
00659    * stopped the vehicle, so autoreplace can start it again */
00660   v->vehstatus |= VS_STOPPED;
00661 }
00662 
00668 static void RunVehicleDayProc()
00669 {
00670   if (_game_mode != GM_NORMAL) return;
00671 
00672   /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
00673   for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00674     Vehicle *v = Vehicle::Get(i);
00675     if (v == NULL) continue;
00676 
00677     /* Call the 32-day callback if needed */
00678     if ((v->day_counter & 0x1F) == 0) {
00679       uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00680       if (callback != CALLBACK_FAILED) {
00681         if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
00682         if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00683       }
00684     }
00685 
00686     /* This is called once per day for each vehicle, but not in the first tick of the day */
00687     v->OnNewDay();
00688   }
00689 }
00690 
00691 void CallVehicleTicks()
00692 {
00693   _vehicles_to_autoreplace.Clear();
00694 
00695   _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
00696 
00697   RunVehicleDayProc();
00698 
00699   Station *st;
00700   FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00701 
00702   Vehicle *v;
00703   FOR_ALL_VEHICLES(v) {
00704     /* Vehicle could be deleted in this tick */
00705     if (!v->Tick()) {
00706       assert(Vehicle::Get(vehicle_index) == NULL);
00707       continue;
00708     }
00709 
00710     assert(Vehicle::Get(vehicle_index) == v);
00711 
00712     switch (v->type) {
00713       default: break;
00714 
00715       case VEH_TRAIN:
00716       case VEH_ROAD:
00717       case VEH_AIRCRAFT:
00718       case VEH_SHIP:
00719         if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00720 
00721         if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00722         if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00723         if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsRoadVehFront()) continue;
00724 
00725         v->motion_counter += v->cur_speed;
00726         /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
00727         if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00728 
00729         /* Play an alterate running sound every 16 ticks */
00730         if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00731     }
00732   }
00733 
00734   for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00735     v = it->first;
00736     /* Autoreplace needs the current company set as the vehicle owner */
00737     _current_company = v->owner;
00738 
00739     /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
00740      * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
00741      * they are already leaving the depot again before being replaced. */
00742     if (it->second) v->vehstatus &= ~VS_STOPPED;
00743 
00744     /* Store the position of the effect as the vehicle pointer will become invalid later */
00745     int x = v->x_pos;
00746     int y = v->y_pos;
00747     int z = v->z_pos;
00748 
00749     const Company *c = Company::Get(_current_company);
00750     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00751     CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00752     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00753 
00754     if (!IsLocalCompany()) continue;
00755 
00756     if (res.Succeeded()) {
00757       ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00758       continue;
00759     }
00760 
00761     StringID error_message = res.GetErrorMessage();
00762     if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00763 
00764     if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00765 
00766     StringID message;
00767     if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00768       message = error_message;
00769     } else {
00770       message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00771     }
00772 
00773     SetDParam(0, v->index);
00774     SetDParam(1, error_message);
00775     AddVehicleNewsItem(message, NS_ADVICE, v->index);
00776   }
00777 
00778   _current_company = OWNER_NONE;
00779 }
00780 
00781 static void DoDrawVehicle(const Vehicle *v)
00782 {
00783   SpriteID image = v->cur_image;
00784   PaletteID pal = PAL_NONE;
00785 
00786   if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00787 
00788   AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00789     v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00790 }
00791 
00792 void ViewportAddVehicles(DrawPixelInfo *dpi)
00793 {
00794   /* The bounding rectangle */
00795   const int l = dpi->left;
00796   const int r = dpi->left + dpi->width;
00797   const int t = dpi->top;
00798   const int b = dpi->top + dpi->height;
00799 
00800   /* The hash area to scan */
00801   int xl, xu, yl, yu;
00802 
00803   if (dpi->width + 70 < (1 << (7 + 6))) {
00804     xl = GB(l - 70, 7, 6);
00805     xu = GB(r,      7, 6);
00806   } else {
00807     /* scan whole hash row */
00808     xl = 0;
00809     xu = 0x3F;
00810   }
00811 
00812   if (dpi->height + 70 < (1 << (6 + 6))) {
00813     yl = GB(t - 70, 6, 6) << 6;
00814     yu = GB(b,      6, 6) << 6;
00815   } else {
00816     /* scan whole column */
00817     yl = 0;
00818     yu = 0x3F << 6;
00819   }
00820 
00821   for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00822     for (int x = xl;; x = (x + 1) & 0x3F) {
00823       const Vehicle *v = _vehicle_position_hash[x + y]; // already masked & 0xFFF
00824 
00825       while (v != NULL) {
00826         if (!(v->vehstatus & VS_HIDDEN) &&
00827             l <= v->coord.right &&
00828             t <= v->coord.bottom &&
00829             r >= v->coord.left &&
00830             b >= v->coord.top) {
00831           DoDrawVehicle(v);
00832         }
00833         v = v->next_hash;
00834       }
00835 
00836       if (x == xu) break;
00837     }
00838 
00839     if (y == yu) break;
00840   }
00841 }
00842 
00843 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00844 {
00845   Vehicle *found = NULL, *v;
00846   uint dist, best_dist = UINT_MAX;
00847 
00848   if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00849 
00850   x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00851   y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00852 
00853   FOR_ALL_VEHICLES(v) {
00854     if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00855         x >= v->coord.left && x <= v->coord.right &&
00856         y >= v->coord.top && y <= v->coord.bottom) {
00857 
00858       dist = max(
00859         abs(((v->coord.left + v->coord.right) >> 1) - x),
00860         abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00861       );
00862 
00863       if (dist < best_dist) {
00864         found = v;
00865         best_dist = dist;
00866       }
00867     }
00868   }
00869 
00870   return found;
00871 }
00872 
00873 void DecreaseVehicleValue(Vehicle *v)
00874 {
00875   v->value -= v->value >> 8;
00876   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00877 }
00878 
00879 static const byte _breakdown_chance[64] = {
00880     3,   3,   3,   3,   3,   3,   3,   3,
00881     4,   4,   5,   5,   6,   6,   7,   7,
00882     8,   8,   9,   9,  10,  10,  11,  11,
00883    12,  13,  13,  13,  13,  14,  15,  16,
00884    17,  19,  21,  25,  28,  31,  34,  37,
00885    40,  44,  48,  52,  56,  60,  64,  68,
00886    72,  80,  90, 100, 110, 120, 130, 140,
00887   150, 170, 190, 210, 230, 250, 250, 250,
00888 };
00889 
00890 void CheckVehicleBreakdown(Vehicle *v)
00891 {
00892   int rel, rel_old;
00893 
00894   /* decrease reliability */
00895   v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
00896   if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00897 
00898   if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
00899       _settings_game.difficulty.vehicle_breakdowns < 1 ||
00900       v->cur_speed < 5 || _game_mode == GM_MENU) {
00901     return;
00902   }
00903 
00904   uint32 r = Random();
00905 
00906   /* increase chance of failure */
00907   int chance = v->breakdown_chance + 1;
00908   if (Chance16I(1, 25, r)) chance += 25;
00909   v->breakdown_chance = min(255, chance);
00910 
00911   /* calculate reliability value to use in comparison */
00912   rel = v->reliability;
00913   if (v->type == VEH_SHIP) rel += 0x6666;
00914 
00915   /* reduced breakdowns? */
00916   if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
00917 
00918   /* check if to break down */
00919   if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
00920     v->breakdown_ctr    = GB(r, 16, 6) + 0x3F;
00921     v->breakdown_delay  = GB(r, 24, 7) + 0x80;
00922     v->breakdown_chance = 0;
00923   }
00924 }
00925 
00926 void AgeVehicle(Vehicle *v)
00927 {
00928   if (v->age < 65535) v->age++;
00929 
00930   int age = v->age - v->max_age;
00931   if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
00932       age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
00933     v->reliability_spd_dec <<= 1;
00934   }
00935 
00936   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00937 
00938   /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
00939   if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
00940 
00941   /* Don't warn if a renew is active */
00942   if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
00943 
00944   StringID str;
00945   if (age == -DAYS_IN_LEAP_YEAR) {
00946     str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
00947   } else if (age == 0) {
00948     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
00949   } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
00950     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
00951   } else {
00952     return;
00953   }
00954 
00955   SetDParam(0, v->index);
00956   AddVehicleNewsItem(str, NS_ADVICE, v->index);
00957 }
00958 
00965 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
00966 {
00967   int count = 0;
00968   int max = 0;
00969   int cars = 0;
00970   int unloading = 0;
00971   bool loading = false;
00972 
00973   const Vehicle *u = v;
00974   /* The station may be NULL when the (colour) string does not need to be set. */
00975   const Station *st = Station::GetIfValid(v->last_station_visited);
00976   assert(colour == NULL || st != NULL);
00977 
00978   /* Count up max and used */
00979   for (; v != NULL; v = v->Next()) {
00980     count += v->cargo.Count();
00981     max += v->cargo_cap;
00982     if (v->cargo_cap != 0 && colour != NULL) {
00983       unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
00984       loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
00985       cars++;
00986     }
00987   }
00988 
00989   if (colour != NULL) {
00990     if (unloading == 0 && loading) {
00991       *colour = STR_PERCENT_UP;
00992     } else if (cars == unloading || !loading) {
00993       *colour = STR_PERCENT_DOWN;
00994     } else {
00995       *colour = STR_PERCENT_UP_DOWN;
00996     }
00997   }
00998 
00999   /* Train without capacity */
01000   if (max == 0) return 100;
01001 
01002   /* Return the percentage */
01003   return (count * 100) / max;
01004 }
01005 
01006 void VehicleEnterDepot(Vehicle *v)
01007 {
01008   /* Always work with the front of the vehicle */
01009   assert(v == v->First());
01010 
01011   switch (v->type) {
01012     case VEH_TRAIN: {
01013       Train *t = Train::From(v);
01014       SetWindowClassesDirty(WC_TRAINS_LIST);
01015       /* Clear path reservation */
01016       SetDepotReservation(t->tile, false);
01017       if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01018 
01019       UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01020       t->wait_counter = 0;
01021       t->force_proceed = 0;
01022       ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01023       t->ConsistChanged(true);
01024       break;
01025     }
01026 
01027     case VEH_ROAD:
01028       SetWindowClassesDirty(WC_ROADVEH_LIST);
01029       break;
01030 
01031     case VEH_SHIP: {
01032       SetWindowClassesDirty(WC_SHIPS_LIST);
01033       Ship *ship = Ship::From(v);
01034       ship->state = TRACK_BIT_DEPOT;
01035       ship->UpdateViewport(true, true);
01036       SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01037       break;
01038     }
01039 
01040     case VEH_AIRCRAFT:
01041       SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01042       HandleAircraftEnterHangar(Aircraft::From(v));
01043       break;
01044     default: NOT_REACHED();
01045   }
01046   SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01047 
01048   if (v->type != VEH_TRAIN) {
01049     /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
01050      * 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 */
01051     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01052   }
01053   SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01054 
01055   v->vehstatus |= VS_HIDDEN;
01056   v->cur_speed = 0;
01057 
01058   VehicleServiceInDepot(v);
01059 
01060   TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01061 
01062   if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01063     SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01064 
01065     const Order *real_order = v->GetOrder(v->cur_order_index);
01066     Order t = v->current_order;
01067     v->current_order.MakeDummy();
01068 
01069     /* Test whether we are heading for this depot. If not, do nothing.
01070      * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
01071     if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01072         real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01073         (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01074       /* We are heading for another depot, keep driving. */
01075       return;
01076     }
01077 
01078     if (t.IsRefit()) {
01079       _current_company = v->owner;
01080       CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01081 
01082       if (cost.Failed()) {
01083         _vehicles_to_autoreplace[v] = false;
01084         if (v->owner == _local_company) {
01085           /* Notify the user that we stopped the vehicle */
01086           SetDParam(0, v->index);
01087           AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01088         }
01089       } else if (cost.GetCost() != 0) {
01090         v->profit_this_year -= cost.GetCost() << 8;
01091         if (v->owner == _local_company) {
01092           ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01093         }
01094       }
01095     }
01096 
01097     if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01098       /* Part of orders */
01099       UpdateVehicleTimetable(v, true);
01100       v->IncrementOrderIndex();
01101     }
01102     if (t.GetDepotActionType() & ODATFB_HALT) {
01103       /* Vehicles are always stopped on entering depots. Do not restart this one. */
01104       _vehicles_to_autoreplace[v] = false;
01105       if (v->owner == _local_company) {
01106         SetDParam(0, v->index);
01107         AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01108       }
01109       AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01110     }
01111   }
01112 }
01113 
01114 
01122 void VehicleMove(Vehicle *v, bool update_viewport)
01123 {
01124   int img = v->cur_image;
01125   Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01126   const Sprite *spr = GetSprite(img, ST_NORMAL);
01127 
01128   pt.x += spr->x_offs;
01129   pt.y += spr->y_offs;
01130 
01131   UpdateVehiclePosHash(v, pt.x, pt.y);
01132 
01133   Rect old_coord = v->coord;
01134   v->coord.left   = pt.x;
01135   v->coord.top    = pt.y;
01136   v->coord.right  = pt.x + spr->width + 2;
01137   v->coord.bottom = pt.y + spr->height + 2;
01138 
01139   if (update_viewport) {
01140     MarkAllViewportsDirty(
01141       min(old_coord.left,   v->coord.left),
01142       min(old_coord.top,    v->coord.top),
01143       max(old_coord.right,  v->coord.right) + 1,
01144       max(old_coord.bottom, v->coord.bottom) + 1
01145     );
01146   }
01147 }
01148 
01157 void MarkSingleVehicleDirty(const Vehicle *v)
01158 {
01159   MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01160 }
01161 
01166 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01167 {
01168   static const int8 _delta_coord[16] = {
01169     -1,-1,-1, 0, 1, 1, 1, 0, /* x */
01170     -1, 0, 1, 1, 1, 0,-1,-1, /* y */
01171   };
01172 
01173   int x = v->x_pos + _delta_coord[v->direction];
01174   int y = v->y_pos + _delta_coord[v->direction + 8];
01175 
01176   GetNewVehiclePosResult gp;
01177   gp.x = x;
01178   gp.y = y;
01179   gp.old_tile = v->tile;
01180   gp.new_tile = TileVirtXY(x, y);
01181   return gp;
01182 }
01183 
01184 static const Direction _new_direction_table[] = {
01185   DIR_N,  DIR_NW, DIR_W,
01186   DIR_NE, DIR_SE, DIR_SW,
01187   DIR_E,  DIR_SE, DIR_S
01188 };
01189 
01190 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01191 {
01192   int i = 0;
01193 
01194   if (y >= v->y_pos) {
01195     if (y != v->y_pos) i += 3;
01196     i += 3;
01197   }
01198 
01199   if (x >= v->x_pos) {
01200     if (x != v->x_pos) i++;
01201     i++;
01202   }
01203 
01204   Direction dir = v->direction;
01205 
01206   DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01207   if (dirdiff == DIRDIFF_SAME) return dir;
01208   return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01209 }
01210 
01220 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01221 {
01222   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01223 }
01224 
01225 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01226 {
01227   /* Find maximum */
01228   const Vehicle *v;
01229   FOR_ALL_VEHICLES(v) {
01230     if (v->type == type && v->owner == owner) {
01231       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01232     }
01233   }
01234 
01235   if (this->maxid == 0) return;
01236 
01237   /* Reserving 'maxid + 2' because we need:
01238    * - space for the last item (with v->unitnumber == maxid)
01239    * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
01240   this->cache = CallocT<bool>(this->maxid + 2);
01241 
01242   /* Fill the cache */
01243   FOR_ALL_VEHICLES(v) {
01244     if (v->type == type && v->owner == owner) {
01245       this->cache[v->unitnumber] = true;
01246     }
01247   }
01248 }
01249 
01250 UnitID FreeUnitIDGenerator::NextID()
01251 {
01252   if (this->maxid <= this->curid) return ++this->curid;
01253 
01254   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
01255 
01256   return this->curid;
01257 }
01258 
01264 UnitID GetFreeUnitNumber(VehicleType type)
01265 {
01266   /* Check whether it is allowed to build another vehicle. */
01267   uint max_veh;
01268   switch (type) {
01269     case VEH_TRAIN:    max_veh = _settings_game.vehicle.max_trains;   break;
01270     case VEH_ROAD:     max_veh = _settings_game.vehicle.max_roadveh;  break;
01271     case VEH_SHIP:     max_veh = _settings_game.vehicle.max_ships;    break;
01272     case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01273     default: NOT_REACHED();
01274   }
01275 
01276   uint amounts[4];
01277   CountCompanyVehicles(_current_company, amounts);
01278   assert((uint)type < lengthof(amounts));
01279   if (amounts[type] >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
01280 
01281   FreeUnitIDGenerator gen(type, _current_company);
01282 
01283   return gen.NextID();
01284 }
01285 
01286 
01295 bool CanBuildVehicleInfrastructure(VehicleType type)
01296 {
01297   assert(IsCompanyBuildableVehicleType(type));
01298 
01299   if (!Company::IsValidID(_local_company)) return false;
01300   if (_settings_client.gui.always_build_infrastructure) return true;
01301 
01302   UnitID max;
01303   switch (type) {
01304     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
01305     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
01306     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
01307     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01308     default: NOT_REACHED();
01309   }
01310 
01311   /* We can build vehicle infrastructure when we may build the vehicle type */
01312   if (max > 0) {
01313     /* Can we actually build the vehicle type? */
01314     const Engine *e;
01315     FOR_ALL_ENGINES_OF_TYPE(e, type) {
01316       if (HasBit(e->company_avail, _local_company)) return true;
01317     }
01318     return false;
01319   }
01320 
01321   /* We should be able to build infrastructure when we have the actual vehicle type */
01322   const Vehicle *v;
01323   FOR_ALL_VEHICLES(v) {
01324     if (v->owner == _local_company && v->type == type) return true;
01325   }
01326 
01327   return false;
01328 }
01329 
01330 
01340 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01341 {
01342   const Company *c = Company::Get(company);
01343   LiveryScheme scheme = LS_DEFAULT;
01344   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01345 
01346   /* The default livery is always available for use, but its in_use flag determines
01347    * whether any _other_ liveries are in use. */
01348   if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01349     /* Determine the livery scheme to use */
01350     const Engine *e = Engine::Get(engine_type);
01351     switch (e->type) {
01352       default: NOT_REACHED();
01353       case VEH_TRAIN: {
01354         if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (Train::From(v)->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01355           /* Wagonoverrides use the coloir scheme of the front engine.
01356            * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
01357           engine_type = parent_engine_type;
01358           e = Engine::Get(engine_type);
01359           /* Note: Luckily cargo_type is not needed for engines */
01360         }
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         if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01365           if (!CargoSpec::Get(cargo_type)->is_freight) {
01366             if (parent_engine_type == INVALID_ENGINE) {
01367               scheme = LS_PASSENGER_WAGON_STEAM;
01368             } else {
01369               switch (RailVehInfo(parent_engine_type)->engclass) {
01370                 default: NOT_REACHED();
01371                 case EC_STEAM:    scheme = LS_PASSENGER_WAGON_STEAM;    break;
01372                 case EC_DIESEL:   scheme = LS_PASSENGER_WAGON_DIESEL;   break;
01373                 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
01374                 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
01375                 case EC_MAGLEV:   scheme = LS_PASSENGER_WAGON_MAGLEV;   break;
01376               }
01377             }
01378           } else {
01379             scheme = LS_FREIGHT_WAGON;
01380           }
01381         } else {
01382           bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01383 
01384           switch (e->u.rail.engclass) {
01385             default: NOT_REACHED();
01386             case EC_STEAM:    scheme = LS_STEAM; break;
01387             case EC_DIESEL:   scheme = is_mu ? LS_DMU : LS_DIESEL;   break;
01388             case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
01389             case EC_MONORAIL: scheme = LS_MONORAIL; break;
01390             case EC_MAGLEV:   scheme = LS_MAGLEV; break;
01391           }
01392         }
01393         break;
01394       }
01395 
01396       case VEH_ROAD: {
01397         /* Always use the livery of the front */
01398         if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01399           engine_type = parent_engine_type;
01400           e = Engine::Get(engine_type);
01401           cargo_type = v->First()->cargo_type;
01402         }
01403         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01404         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01405 
01406         /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
01407         if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01408           /* Tram */
01409           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01410         } else {
01411           /* Bus or truck */
01412           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01413         }
01414         break;
01415       }
01416 
01417       case VEH_SHIP: {
01418         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01419         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01420         scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01421         break;
01422       }
01423 
01424       case VEH_AIRCRAFT: {
01425         switch (e->u.air.subtype) {
01426           case AIR_HELI: scheme = LS_HELICOPTER; break;
01427           case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
01428           case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
01429         }
01430         break;
01431       }
01432     }
01433 
01434     /* Switch back to the default scheme if the resolved scheme is not in use */
01435     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01436   }
01437 
01438   return &c->livery[scheme];
01439 }
01440 
01441 
01442 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01443 {
01444   PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01445 
01446   /* Return cached value if any */
01447   if (map != PAL_NONE) return map;
01448 
01449   const Engine *e = Engine::Get(engine_type);
01450 
01451   /* Check if we should use the colour map callback */
01452   if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01453     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01454     /* A return value of 0xC000 is stated to "use the default two-colour
01455      * maps" which happens to be the failure action too... */
01456     if (callback != CALLBACK_FAILED && callback != 0xC000) {
01457       map = GB(callback, 0, 14);
01458       /* If bit 14 is set, then the company colours are applied to the
01459        * map else it's returned as-is. */
01460       if (!HasBit(callback, 14)) {
01461         /* Update cache */
01462         if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01463         return map;
01464       }
01465     }
01466   }
01467 
01468   bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01469 
01470   if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01471 
01472   /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
01473   if (!Company::IsValidID(company)) return map;
01474 
01475   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01476 
01477   map += livery->colour1;
01478   if (twocc) map += livery->colour2 * 16;
01479 
01480   /* Update cache */
01481   if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01482   return map;
01483 }
01484 
01485 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01486 {
01487   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01488 }
01489 
01490 PaletteID GetVehiclePalette(const Vehicle *v)
01491 {
01492   if (v->type == VEH_TRAIN) {
01493     return GetEngineColourMap(v->engine_type, v->owner, Train::From(v)->tcache.first_engine, v);
01494   } else if (v->type == VEH_ROAD) {
01495     return GetEngineColourMap(v->engine_type, v->owner, RoadVehicle::From(v)->rcache.first_engine, v);
01496   }
01497 
01498   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01499 }
01500 
01509 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01510 {
01511   if (mail_capacity != NULL) *mail_capacity = 0;
01512   const Engine *e = Engine::Get(v->engine_type);
01513 
01514   if (!e->CanCarryCargo()) return 0;
01515 
01516   if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01517     *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01518   }
01519   CargoID default_cargo = e->GetDefaultCargoType();
01520 
01521   /* Check the refit capacity callback if we are not in the default configuration.
01522    * Note: This might change to become more consistent/flexible/sane, esp. when default cargo is first refittable. */
01523   if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01524       (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01525     uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01526     if (callback != CALLBACK_FAILED) return callback;
01527   }
01528 
01529   /* Get capacity according to property resp. CB */
01530   uint capacity;
01531   switch (e->type) {
01532     case VEH_TRAIN:    capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY,        e->u.rail.capacity); break;
01533     case VEH_ROAD:     capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY,      e->u.road.capacity); break;
01534     case VEH_SHIP:     capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY,         e->u.ship.capacity); break;
01535     case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01536     default: NOT_REACHED();
01537   }
01538 
01539   /* Apply multipliers depending on cargo- and vehicletype.
01540    * Note: This might change to become more consistent/flexible. */
01541   if (e->type != VEH_SHIP) {
01542     if (e->type == VEH_AIRCRAFT) {
01543       if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01544         capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01545       }
01546       if (v->cargo_type == CT_MAIL) return capacity;
01547     } else {
01548       switch (default_cargo) {
01549         case CT_PASSENGERS: break;
01550         case CT_MAIL:
01551         case CT_GOODS: capacity *= 2; break;
01552         default:       capacity *= 4; break;
01553       }
01554     }
01555     switch (v->cargo_type) {
01556       case CT_PASSENGERS: break;
01557       case CT_MAIL:
01558       case CT_GOODS: capacity /= 2; break;
01559       default:       capacity /= 4; break;
01560     }
01561   }
01562 
01563   return capacity;
01564 }
01565 
01566 
01567 void Vehicle::BeginLoading()
01568 {
01569   assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01570 
01571   if (this->current_order.IsType(OT_GOTO_STATION) &&
01572       this->current_order.GetDestination() == this->last_station_visited) {
01573     current_order.MakeLoading(true);
01574     UpdateVehicleTimetable(this, true);
01575 
01576     /* Furthermore add the Non Stop flag to mark that this station
01577      * is the actual destination of the vehicle, which is (for example)
01578      * necessary to be known for HandleTrainLoading to determine
01579      * whether the train is lost or not; not marking a train lost
01580      * that arrives at random stations is bad. */
01581     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01582 
01583   } else {
01584     current_order.MakeLoading(false);
01585   }
01586 
01587   Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01588 
01589   PrepareUnload(this);
01590 
01591   SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01592   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01593   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01594   SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01595 
01596   Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01597   this->cur_speed = 0;
01598   this->MarkDirty();
01599 }
01600 
01601 void Vehicle::LeaveStation()
01602 {
01603   assert(current_order.IsType(OT_LOADING));
01604 
01605   delete this->cargo_payment;
01606 
01607   /* Only update the timetable if the vehicle was supposed to stop here. */
01608   if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01609 
01610   current_order.MakeLeaveStation();
01611   Station *st = Station::Get(this->last_station_visited);
01612   st->loading_vehicles.remove(this);
01613 
01614   HideFillingPercent(&this->fill_percent_te_id);
01615 
01616   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01617     /* Trigger station animation (trains only) */
01618     if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
01619 
01620     /* Try to reserve a path when leaving the station as we
01621      * might not be marked as wanting a reservation, e.g.
01622      * when an overlength train gets turned around in a station. */
01623     if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(this->GetVehicleTrackdir()), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
01624       TryPathReserve(Train::From(this), true, true);
01625     }
01626   }
01627 }
01628 
01629 
01630 void Vehicle::HandleLoading(bool mode)
01631 {
01632   switch (this->current_order.GetType()) {
01633     case OT_LOADING: {
01634       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01635 
01636       /* Not the first call for this tick, or still loading */
01637       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01638           (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01639 
01640       this->PlayLeaveStationSound();
01641 
01642       bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
01643       this->LeaveStation();
01644 
01645       /* If this was not the final order, don't remove it from the list. */
01646       if (!at_destination_station) return;
01647       break;
01648     }
01649 
01650     case OT_DUMMY: break;
01651 
01652     default: return;
01653   }
01654 
01655   this->IncrementOrderIndex();
01656 }
01657 
01658 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01659 {
01660   if (!CheckOwnership(this->owner)) return CMD_ERROR;
01661   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01662   if (this->IsStoppedInDepot()) return CMD_ERROR;
01663 
01664   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01665     bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01666     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01667       /* We called with a different DEPOT_SERVICE setting.
01668        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
01669        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
01670       if (flags & DC_EXEC) {
01671         this->current_order.SetDepotOrderType(ODTF_MANUAL);
01672         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01673         SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01674       }
01675       return CommandCost();
01676     }
01677 
01678     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
01679     if (flags & DC_EXEC) {
01680       /* If the orders to 'goto depot' are in the orders list (forced servicing),
01681        * then skip to the next order; effectively cancelling this forced service */
01682       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
01683 
01684       this->current_order.MakeDummy();
01685       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01686     }
01687     return CommandCost();
01688   }
01689 
01690   TileIndex location;
01691   DestinationID destination;
01692   bool reverse;
01693   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};
01694   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01695 
01696   if (flags & DC_EXEC) {
01697     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01698 
01699     this->dest_tile = location;
01700     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01701     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01702     SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01703 
01704     /* If there is no depot in front, reverse automatically (trains only) */
01705     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01706 
01707     if (this->type == VEH_AIRCRAFT) {
01708       Aircraft *a = Aircraft::From(this);
01709       if (a->state == FLYING && a->targetairport != destination) {
01710         /* The aircraft is now heading for a different hangar than the next in the orders */
01711         extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01712         AircraftNextAirportPos_and_Order(a);
01713       }
01714     }
01715   }
01716 
01717   return CommandCost();
01718 
01719 }
01720 
01721 void Vehicle::SetNext(Vehicle *next)
01722 {
01723   assert(this != next);
01724 
01725   if (this->next != NULL) {
01726     /* We had an old next vehicle. Update the first and previous pointers */
01727     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01728       v->first = this->next;
01729     }
01730     this->next->previous = NULL;
01731   }
01732 
01733   this->next = next;
01734 
01735   if (this->next != NULL) {
01736     /* A new next vehicle. Update the first and previous pointers */
01737     if (this->next->previous != NULL) this->next->previous->next = NULL;
01738     this->next->previous = this;
01739     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01740       v->first = this->first;
01741     }
01742   }
01743 }
01744 
01745 void Vehicle::AddToShared(Vehicle *shared_chain)
01746 {
01747   assert(this->previous_shared == NULL && this->next_shared == NULL);
01748 
01749   if (!shared_chain->orders.list) {
01750     assert(shared_chain->previous_shared == NULL);
01751     assert(shared_chain->next_shared == NULL);
01752     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
01753   }
01754 
01755   this->next_shared     = shared_chain->next_shared;
01756   this->previous_shared = shared_chain;
01757 
01758   shared_chain->next_shared = this;
01759 
01760   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
01761 
01762   shared_chain->orders.list->AddVehicle(this);
01763 }
01764 
01765 void Vehicle::RemoveFromShared()
01766 {
01767   /* Remember if we were first and the old window number before RemoveVehicle()
01768    * as this changes first if needed. */
01769   bool were_first = (this->FirstShared() == this);
01770   uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
01771 
01772   this->orders.list->RemoveVehicle(this);
01773 
01774   if (!were_first) {
01775     /* We are not the first shared one, so only relink our previous one. */
01776     this->previous_shared->next_shared = this->NextShared();
01777   }
01778 
01779   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
01780 
01781 
01782   if (this->orders.list->GetNumVehicles() == 1) {
01783     /* When there is only one vehicle, remove the shared order list window. */
01784     DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
01785     InvalidateVehicleOrder(this->FirstShared(), 0);
01786   } else if (were_first) {
01787     /* If we were the first one, update to the new first one.
01788      * Note: FirstShared() is already the new first */
01789     InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
01790   }
01791 
01792   this->next_shared     = NULL;
01793   this->previous_shared = NULL;
01794 }
01795 
01796 void StopAllVehicles()
01797 {
01798   Vehicle *v;
01799   FOR_ALL_VEHICLES(v) {
01800     /* Code ripped from CmdStartStopTrain. Can't call it, because of
01801      * ownership problems, so we'll duplicate some code, for now */
01802     v->vehstatus |= VS_STOPPED;
01803     v->MarkDirty();
01804     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01805     SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01806   }
01807 }
01808 
01809 void VehiclesYearlyLoop()
01810 {
01811   Vehicle *v;
01812   FOR_ALL_VEHICLES(v) {
01813     if (v->IsPrimaryVehicle()) {
01814       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
01815       Money profit = v->GetDisplayProfitThisYear();
01816       if (v->age >= 730 && profit < 0) {
01817         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
01818           SetDParam(0, v->index);
01819           SetDParam(1, profit);
01820           AddVehicleNewsItem(
01821             STR_NEWS_VEHICLE_IS_UNPROFITABLE,
01822             NS_ADVICE,
01823             v->index
01824           );
01825         }
01826         AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
01827       }
01828 
01829       v->profit_last_year = v->profit_this_year;
01830       v->profit_this_year = 0;
01831       SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01832     }
01833   }
01834 }
01835 
01836 
01846 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
01847 {
01848   const Engine *e = Engine::GetIfValid(engine_type);
01849   assert(e != NULL);
01850 
01851   switch (e->type) {
01852     case VEH_TRAIN:
01853       return (st->facilities & FACIL_TRAIN) != 0;
01854 
01855     case VEH_ROAD:
01856       /* For road vehicles we need the vehicle to know whether it can actually
01857        * use the station, but if it doesn't have facilities for RVs it is
01858        * certainly not possible that the station can be used. */
01859       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
01860 
01861     case VEH_SHIP:
01862       return (st->facilities & FACIL_DOCK) != 0;
01863 
01864     case VEH_AIRCRAFT:
01865       return (st->facilities & FACIL_AIRPORT) != 0 &&
01866           (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
01867 
01868     default:
01869       return false;
01870   }
01871 }
01872 
01879 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
01880 {
01881   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
01882 
01883   return CanVehicleUseStation(v->engine_type, st);
01884 }

Generated on Mon Aug 30 19:37:03 2010 for OpenTTD by  doxygen 1.6.1