# vehicle.cpp

Go to the documentation of this file.

    00001 /* $Id: vehicle.cpp 25241 2013-05-13 19:22:08Z 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 "error.h"
    00014 #include "roadveh.h"
    00015 #include "ship.h"
    00016 #include "spritecache.h"
    00017 #include "timetable.h"
    00018 #include "viewport_func.h"
    00019 #include "news_func.h"
    00020 #include "command_func.h"
    00021 #include "company_func.h"
    00022 #include "train.h"
    00023 #include "aircraft.h"
    00024 #include "newgrf_debug.h"
    00025 #include "newgrf_sound.h"
    00026 #include "newgrf_station.h"
    00027 #include "group_gui.h"
    00028 #include "strings_func.h"
    00029 #include "zoom_func.h"
    00030 #include "date_func.h"
    00031 #include "vehicle_func.h"
    00032 #include "autoreplace_func.h"
    00033 #include "autoreplace_gui.h"
    00034 #include "station_base.h"
    00035 #include "ai/ai.hpp"
    00036 #include "depot_func.h"
    00037 #include "network/network.h"
    00038 #include "core/pool_func.hpp"
    00039 #include "economy_base.h"
    00040 #include "articulated_vehicles.h"
    00041 #include "roadstop_base.h"
    00042 #include "core/random_func.hpp"
    00043 #include "core/backup_type.hpp"
    00044 #include "order_backup.h"
    00045 #include "sound_func.h"
    00046 #include "effectvehicle_func.h"
    00047 #include "effectvehicle_base.h"
    00048 #include "vehiclelist.h"
    00049 #include "bridge_map.h"
    00050 #include "tunnel_map.h"
    00051 #include "depot_map.h"
    00052 #include "gamelog.h"
    00053
    00054 #include "table/strings.h"
    00055
    00056 #define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
    00057
    00058 VehicleID _new_vehicle_id;
    00059 uint16 _returned_refit_capacity;
    00060 uint16 _returned_mail_refit_capacity;
    00061
    00062
    00064 VehiclePool _vehicle_pool("Vehicle");
    00065 INSTANTIATE_POOL_METHODS(Vehicle)
    00066
    00067
    00073 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) 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 (use_renew_setting && !c->settings.engine_renew) return false;
    00082   if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
    00083
    00084   /* Only engines need renewing */
    00085   if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
    00086
    00087   return true;
    00088 }
    00089
    00090 void VehicleServiceInDepot(Vehicle *v)
    00091 {
    00092   v->date_of_last_service = _date;
    00093   v->breakdowns_since_last_service = 0;
    00094   v->reliability = v->GetEngine()->reliability;
    00095   /* Prevent vehicles from breaking down directly after exiting the depot. */
    00096   v->breakdown_chance /= 4;
    00097   SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
    00098 }
    00099
    00106 bool Vehicle::NeedsServicing() const
    00107 {
    00108   /* Stopped or crashed vehicles will not move, as such making unmovable
    00109    * vehicles to go for service is lame. */
    00110   if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
    00111
    00112   /* Are we ready for the next service cycle? */
    00113   const Company *c = Company::Get(this->owner);
    00114   if (this->ServiceIntervalIsPercent() ?
    00115       (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
    00116       (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
    00117     return false;
    00118   }
    00119
    00120   /* If we're servicing anyway, because we have not disabled servicing when
    00121    * there are no breakdowns or we are playing with breakdowns, bail out. */
    00122   if (!_settings_game.order.no_servicing_if_no_breakdowns ||
    00123       _settings_game.difficulty.vehicle_breakdowns != 0) {
    00124     return true;
    00125   }
    00126
    00127   /* Test whether there is some pending autoreplace.
    00128    * Note: We do this after the service-interval test.
    00129    * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
    00130   bool pending_replace = false;
    00131   Money needed_money = c->settings.engine_renew_money;
    00132   if (needed_money > c->money) return false;
    00133
    00134   for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
    00135     bool replace_when_old = false;
    00136     EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
    00137
    00138     /* Check engine availability */
    00139     if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
    00140     /* Is the vehicle old if we are not always replacing? */
    00141     if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
    00142
    00143     /* Check refittability */
    00144     uint32 available_cargo_types, union_mask;
    00145     GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
    00146     /* Is there anything to refit? */
    00147     if (union_mask != 0) {
    00148       CargoID cargo_type;
    00149       /* We cannot refit to mixed cargoes in an automated way */
    00150       if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
    00151
    00152       /* Did the old vehicle carry anything? */
    00153       if (cargo_type != CT_INVALID) {
    00154         /* We can't refit the vehicle to carry the cargo we want */
    00155         if (!HasBit(available_cargo_types, cargo_type)) continue;
    00156       }
    00157     }
    00158
    00159     /* Check money.
    00160      * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
    00161     pending_replace = true;
    00162     needed_money += 2 * Engine::Get(new_engine)->GetCost();
    00163     if (needed_money > c->money) return false;
    00164   }
    00165
    00166   return pending_replace;
    00167 }
    00168
    00174 bool Vehicle::NeedsAutomaticServicing() const
    00175 {
    00176   if (this->HasDepotOrder()) return false;
    00177   if (this->current_order.IsType(OT_LOADING)) return false;
    00178   if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
    00179   return NeedsServicing();
    00180 }
    00181
    00182 uint Vehicle::Crash(bool flooded)
    00183 {
    00184   assert((this->vehstatus & VS_CRASHED) == 0);
    00185   assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
    00186
    00187   uint pass = 0;
    00188   /* Stop the vehicle. */
    00189   if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
    00190   /* crash all wagons, and count passengers */
    00191   for (Vehicle *v = this; v != NULL; v = v->Next()) {
    00192     if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
    00193     v->vehstatus |= VS_CRASHED;
    00194     MarkSingleVehicleDirty(v);
    00195   }
    00196
    00197   /* Dirty some windows */
    00198   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
    00199   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
    00200   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
    00201   SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
    00202
    00203   delete this->cargo_payment;
    00204   this->cargo_payment = NULL;
    00205
    00206   return RandomRange(pass + 1); // Randomise deceased passengers.
    00207 }
    00208
    00209
    00218 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
    00219 {
    00220   const Engine *e = Engine::Get(engine);
    00221   GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
    00222
    00223   if (!HasBit(grfconfig->grf_bugs, bug_type)) {
    00224     SetBit(grfconfig->grf_bugs, bug_type);
    00225     SetDParamStr(0, grfconfig->GetName());
    00226     SetDParam(1, engine);
    00227     ShowErrorMessage(part1, part2, WL_CRITICAL);
    00228     if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
    00229   }
    00230
    00231   /* debug output */
    00232   char buffer[512];
    00233
    00234   SetDParamStr(0, grfconfig->GetName());
    00235   GetString(buffer, part1, lastof(buffer));
    00236   DEBUG(grf, 0, "%s", buffer + 3);
    00237
    00238   SetDParam(1, engine);
    00239   GetString(buffer, part2, lastof(buffer));
    00240   DEBUG(grf, 0, "%s", buffer + 3);
    00241 }
    00242
    00248 void VehicleLengthChanged(const Vehicle *u)
    00249 {
    00250   /* show a warning once for each engine in whole game and once for each GRF after each game load */
    00251   const Engine *engine = u->GetEngine();
    00252   uint32 grfid = engine->grf_prop.grffile->grfid;
    00253   GRFConfig *grfconfig = GetGRFConfig(grfid);
    00254   if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
    00255     ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
    00256   }
    00257 }
    00258
    00263 Vehicle::Vehicle(VehicleType type)
    00264 {
    00265   this->type               = type;
    00266   this->coord.left         = INVALID_COORD;
    00267   this->group_id           = DEFAULT_GROUP;
    00268   this->fill_percent_te_id = INVALID_TE_ID;
    00269   this->first              = this;
    00270   this->colourmap          = PAL_NONE;
    00271   this->cargo_age_counter  = 1;
    00272 }
    00273
    00278 byte VehicleRandomBits()
    00279 {
    00280   return GB(Random(), 0, 8);
    00281 }
    00282
    00283 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
    00284  * lookup times at the expense of memory usage. */
    00285 const int HASH_BITS = 7;
    00286 const int HASH_SIZE = 1 << HASH_BITS;
    00287 const int HASH_MASK = HASH_SIZE - 1;
    00288 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
    00289 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
    00290
    00291 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
    00292  * Profiling results show that 0 is fastest. */
    00293 const int HASH_RES = 0;
    00294
    00295 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
    00296
    00297 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
    00298 {
    00299   for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
    00300     for (int x = xl; ; x = (x + 1) & HASH_MASK) {
    00301       Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
    00302       for (; v != NULL; v = v->hash_tile_next) {
    00303         Vehicle *a = proc(v, data);
    00304         if (find_first && a != NULL) return a;
    00305       }
    00306       if (x == xu) break;
    00307     }
    00308     if (y == yu) break;
    00309   }
    00310
    00311   return NULL;
    00312 }
    00313
    00314
    00326 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
    00327 {
    00328   const int COLL_DIST = 6;
    00329
    00330   /* Hash area to scan is from xl,yl to xu,yu */
    00331   int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
    00332   int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
    00333   int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
    00334   int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
    00335
    00336   return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
    00337 }
    00338
    00353 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
    00354 {
    00355   VehicleFromPosXY(x, y, data, proc, false);
    00356 }
    00357
    00369 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
    00370 {
    00371   return VehicleFromPosXY(x, y, data, proc, true) != NULL;
    00372 }
    00373
    00384 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
    00385 {
    00386   int x = GB(TileX(tile), HASH_RES, HASH_BITS);
    00387   int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
    00388
    00389   Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
    00390   for (; v != NULL; v = v->hash_tile_next) {
    00391     if (v->tile != tile) continue;
    00392
    00393     Vehicle *a = proc(v, data);
    00394     if (find_first && a != NULL) return a;
    00395   }
    00396
    00397   return NULL;
    00398 }
    00399
    00413 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
    00414 {
    00415   VehicleFromPos(tile, data, proc, false);
    00416 }
    00417
    00428 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
    00429 {
    00430   return VehicleFromPos(tile, data, proc, true) != NULL;
    00431 }
    00432
    00439 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
    00440 {
    00441   int z = *(int*)data;
    00442
    00443   if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
    00444   if (v->z_pos > z) return NULL;
    00445
    00446   return v;
    00447 }
    00448
    00454 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
    00455 {
    00456   int z = GetTileMaxPixelZ(tile);
    00457
    00458   /* Value v is not safe in MP games, however, it is used to generate a local
    00459    * error message only (which may be different for different machines).
    00460    * Such a message does not affect MP synchronisation.
    00461    */
    00462   Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
    00463   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
    00464   return CommandCost();
    00465 }
    00466
    00468 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
    00469 {
    00470   if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
    00471   if (v == (const Vehicle *)data) return NULL;
    00472
    00473   return v;
    00474 }
    00475
    00483 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
    00484 {
    00485   /* Value v is not safe in MP games, however, it is used to generate a local
    00486    * error message only (which may be different for different machines).
    00487    * Such a message does not affect MP synchronisation.
    00488    */
    00489   Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
    00490   if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
    00491
    00492   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
    00493   return CommandCost();
    00494 }
    00495
    00496 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
    00497 {
    00498   TrackBits rail_bits = *(TrackBits *)data;
    00499
    00500   if (v->type != VEH_TRAIN) return NULL;
    00501
    00502   Train *t = Train::From(v);
    00503   if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
    00504
    00505   return v;
    00506 }
    00507
    00516 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
    00517 {
    00518   /* Value v is not safe in MP games, however, it is used to generate a local
    00519    * error message only (which may be different for different machines).
    00520    * Such a message does not affect MP synchronisation.
    00521    */
    00522   Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
    00523   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
    00524   return CommandCost();
    00525 }
    00526
    00527 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
    00528 {
    00529   Vehicle **old_hash = v->hash_tile_current;
    00530   Vehicle **new_hash;
    00531
    00532   if (remove) {
    00533     new_hash = NULL;
    00534   } else {
    00535     int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
    00536     int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
    00537     new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
    00538   }
    00539
    00540   if (old_hash == new_hash) return;
    00541
    00542   /* Remove from the old position in the hash table */
    00543   if (old_hash != NULL) {
    00544     if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
    00545     *v->hash_tile_prev = v->hash_tile_next;
    00546   }
    00547
    00548   /* Insert vehicle at beginning of the new position in the hash table */
    00549   if (new_hash != NULL) {
    00550     v->hash_tile_next = *new_hash;
    00551     if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
    00552     v->hash_tile_prev = new_hash;
    00553     *new_hash = v;
    00554   }
    00555
    00556   /* Remember current hash position */
    00557   v->hash_tile_current = new_hash;
    00558 }
    00559
    00560 static Vehicle *_vehicle_viewport_hash[0x1000];
    00561
    00562 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
    00563 {
    00564   Vehicle **old_hash, **new_hash;
    00565   int old_x = v->coord.left;
    00566   int old_y = v->coord.top;
    00567
    00568   new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
    00569   old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
    00570
    00571   if (old_hash == new_hash) return;
    00572
    00573   /* remove from hash table? */
    00574   if (old_hash != NULL) {
    00575     if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
    00576     *v->hash_viewport_prev = v->hash_viewport_next;
    00577   }
    00578
    00579   /* insert into hash table? */
    00580   if (new_hash != NULL) {
    00581     v->hash_viewport_next = *new_hash;
    00582     if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
    00583     v->hash_viewport_prev = new_hash;
    00584     *new_hash = v;
    00585   }
    00586 }
    00587
    00588 void ResetVehicleHash()
    00589 {
    00590   Vehicle *v;
    00591   FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
    00592   memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
    00593   memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
    00594 }
    00595
    00596 void ResetVehicleColourMap()
    00597 {
    00598   Vehicle *v;
    00599   FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
    00600 }
    00601
    00606 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
    00607 static AutoreplaceMap _vehicles_to_autoreplace;
    00608
    00609 void InitializeVehicles()
    00610 {
    00611   _vehicles_to_autoreplace.Reset();
    00612   ResetVehicleHash();
    00613 }
    00614
    00615 uint CountVehiclesInChain(const Vehicle *v)
    00616 {
    00617   uint count = 0;
    00618   do count++; while ((v = v->Next()) != NULL);
    00619   return count;
    00620 }
    00621
    00626 bool Vehicle::IsEngineCountable() const
    00627 {
    00628   switch (this->type) {
    00629     case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
    00630     case VEH_TRAIN:
    00631       return !this->IsArticulatedPart() && // tenders and other articulated parts
    00632           !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
    00633     case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
    00634     case VEH_SHIP: return true;
    00635     default: return false; // Only count company buildable vehicles
    00636   }
    00637 }
    00638
    00643 bool Vehicle::HasEngineType() const
    00644 {
    00645   switch (this->type) {
    00646     case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
    00647     case VEH_TRAIN:
    00648     case VEH_ROAD:
    00649     case VEH_SHIP: return true;
    00650     default: return false;
    00651   }
    00652 }
    00653
    00659 const Engine *Vehicle::GetEngine() const
    00660 {
    00661   return Engine::Get(this->engine_type);
    00662 }
    00663
    00669 const GRFFile *Vehicle::GetGRF() const
    00670 {
    00671   return this->GetEngine()->GetGRF();
    00672 }
    00673
    00679 uint32 Vehicle::GetGRFID() const
    00680 {
    00681   return this->GetEngine()->GetGRFID();
    00682 }
    00683
    00691 void Vehicle::HandlePathfindingResult(bool path_found)
    00692 {
    00693   if (path_found) {
    00694     /* Route found, is the vehicle marked with "lost" flag? */
    00695     if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
    00696
    00697     /* Clear the flag as the PF's problem was solved. */
    00698     ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
    00699     /* Delete the news item. */
    00700     DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
    00701     return;
    00702   }
    00703
    00704   /* Were we already lost? */
    00705   if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
    00706
    00707   /* It is first time the problem occurred, set the "lost" flag. */
    00708   SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
    00709   /* Notify user about the event. */
    00710   AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
    00711   if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
    00712     SetDParam(0, this->index);
    00713     AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
    00714   }
    00715 }
    00716
    00718 void Vehicle::PreDestructor()
    00719 {
    00720   if (CleaningPool()) return;
    00721
    00722   if (Station::IsValidID(this->last_station_visited)) {
    00723     Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
    00724
    00725     HideFillingPercent(&this->fill_percent_te_id);
    00726
    00727     delete this->cargo_payment;
    00728   }
    00729
    00730   if (this->IsEngineCountable()) {
    00731     GroupStatistics::CountEngine(this, -1);
    00732     if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
    00733     GroupStatistics::UpdateAutoreplace(this->owner);
    00734
    00735     if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
    00736     DeleteGroupHighlightOfVehicle(this);
    00737   }
    00738
    00739   if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
    00740     Aircraft *a = Aircraft::From(this);
    00741     Station *st = GetTargetAirportIfValid(a);
    00742     if (st != NULL) {
    00743       const AirportFTA *layout = st->airport.GetFTA()->layout;
    00744       CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
    00745     }
    00746   }
    00747
    00748
    00749   if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
    00750     RoadVehicle *v = RoadVehicle::From(this);
    00751     if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
    00752       /* Leave the drive through roadstop, when you have not already left it. */
    00753       RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
    00754     }
    00755   }
    00756
    00757   if (this->Previous() == NULL) {
    00758     InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
    00759   }
    00760
    00761   if (this->IsPrimaryVehicle()) {
    00762     DeleteWindowById(WC_VEHICLE_VIEW, this->index);
    00763     DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
    00764     DeleteWindowById(WC_VEHICLE_REFIT, this->index);
    00765     DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
    00766     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
    00767     SetWindowDirty(WC_COMPANY, this->owner);
    00768     OrderBackup::ClearVehicle(this);
    00769   }
    00770   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
    00771
    00772   this->cargo.Truncate(0);
    00773   DeleteVehicleOrders(this);
    00774   DeleteDepotHighlightOfVehicle(this);
    00775
    00776   extern void StopGlobalFollowVehicle(const Vehicle *v);
    00777   StopGlobalFollowVehicle(this);
    00778
    00779   ReleaseDisastersTargetingVehicle(this->index);
    00780 }
    00781
    00782 Vehicle::~Vehicle()
    00783 {
    00784   if (CleaningPool()) {
    00785     this->cargo.OnCleanPool();
    00786     return;
    00787   }
    00788
    00789   /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
    00790    * it may happen that vehicle chain is deleted when visible */
    00791   if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
    00792
    00793   Vehicle *v = this->Next();
    00794   this->SetNext(NULL);
    00795
    00796   delete v;
    00797
    00798   UpdateVehicleTileHash(this, true);
    00799   UpdateVehicleViewportHash(this, INVALID_COORD, 0);
    00800   DeleteVehicleNews(this->index, INVALID_STRING_ID);
    00801   DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
    00802 }
    00803
    00808 void VehicleEnteredDepotThisTick(Vehicle *v)
    00809 {
    00810   /* Vehicle should stop in the depot if it was in 'stopping' state */
    00811   _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
    00812
    00813   /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
    00814    * stopping in the depot, so we stop it to ensure that it will not reserve
    00815    * the path out of the depot before we might autoreplace it to a different
    00816    * engine. The new engine would not own the reserved path we store that we
    00817    * stopped the vehicle, so autoreplace can start it again */
    00818   v->vehstatus |= VS_STOPPED;
    00819 }
    00820
    00826 static void RunVehicleDayProc()
    00827 {
    00828   if (_game_mode != GM_NORMAL) return;
    00829
    00830   /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
    00831   for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
    00832     Vehicle *v = Vehicle::Get(i);
    00833     if (v == NULL) continue;
    00834
    00835     /* Call the 32-day callback if needed */
    00836     if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
    00837       uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
    00838       if (callback != CALLBACK_FAILED) {
    00839         if (HasBit(callback, 0)) {
    00840           /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
    00841           TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
    00842           v->MarkDirty();
    00843         }
    00844         if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
    00845
    00846         if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
    00847       }
    00848     }
    00849
    00850     /* This is called once per day for each vehicle, but not in the first tick of the day */
    00851     v->OnNewDay();
    00852   }
    00853 }
    00854
    00855 void CallVehicleTicks()
    00856 {
    00857   _vehicles_to_autoreplace.Clear();
    00858
    00859   RunVehicleDayProc();
    00860
    00861   Station *st;
    00862   FOR_ALL_STATIONS(st) LoadUnloadStation(st);
    00863
    00864   Vehicle *v;
    00865   FOR_ALL_VEHICLES(v) {
    00866     /* Vehicle could be deleted in this tick */
    00867     if (!v->Tick()) {
    00868       assert(Vehicle::Get(vehicle_index) == NULL);
    00869       continue;
    00870     }
    00871
    00872     assert(Vehicle::Get(vehicle_index) == v);
    00873
    00874     switch (v->type) {
    00875       default: break;
    00876
    00877       case VEH_TRAIN:
    00878       case VEH_ROAD:
    00879       case VEH_AIRCRAFT:
    00880       case VEH_SHIP: {
    00881         Vehicle *front = v->First();
    00882
    00883         if (v->vcache.cached_cargo_age_period != 0) {
    00884           v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
    00885           if (--v->cargo_age_counter == 0) {
    00886             v->cargo.AgeCargo();
    00887             v->cargo_age_counter = v->vcache.cached_cargo_age_period;
    00888           }
    00889         }
    00890
    00891         /* Do not play any sound when crashed */
    00892         if (front->vehstatus & VS_CRASHED) continue;
    00893
    00894         /* Do not play any sound when in depot or tunnel */
    00895         if (v->vehstatus & VS_HIDDEN) continue;
    00896
    00897         /* Do not play any sound when stopped */
    00898         if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
    00899
    00900         /* Check vehicle type specifics */
    00901         switch (v->type) {
    00902           case VEH_TRAIN:
    00903             if (Train::From(v)->IsWagon()) continue;
    00904             break;
    00905
    00906           case VEH_ROAD:
    00907             if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
    00908             break;
    00909
    00910           case VEH_AIRCRAFT:
    00911             if (!Aircraft::From(v)->IsNormalAircraft()) continue;
    00912             break;
    00913
    00914           default:
    00915             break;
    00916         }
    00917
    00918         v->motion_counter += front->cur_speed;
    00919         /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
    00920         if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
    00921
    00922         /* Play an alternating running sound every 16 ticks */
    00923         if (GB(v->tick_counter, 0, 4) == 0) {
    00924           /* Play running sound when speed > 0 and not braking */
    00925           bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
    00926           PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
    00927         }
    00928
    00929         break;
    00930       }
    00931     }
    00932   }
    00933
    00934   Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
    00935   for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
    00936     v = it->first;
    00937     /* Autoreplace needs the current company set as the vehicle owner */
    00938     cur_company.Change(v->owner);
    00939
    00940     /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
    00941      * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
    00942      * they are already leaving the depot again before being replaced. */
    00943     if (it->second) v->vehstatus &= ~VS_STOPPED;
    00944
    00945     /* Store the position of the effect as the vehicle pointer will become invalid later */
    00946     int x = v->x_pos;
    00947     int y = v->y_pos;
    00948     int z = v->z_pos;
    00949
    00950     const Company *c = Company::Get(_current_company);
    00951     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
    00952     CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
    00953     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
    00954
    00955     if (!IsLocalCompany()) continue;
    00956
    00957     if (res.Succeeded()) {
    00958       ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
    00959       continue;
    00960     }
    00961
    00962     StringID error_message = res.GetErrorMessage();
    00963     if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
    00964
    00965     if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
    00966
    00967     StringID message;
    00968     if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
    00969       message = error_message;
    00970     } else {
    00971       message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
    00972     }
    00973
    00974     SetDParam(0, v->index);
    00975     SetDParam(1, error_message);
    00976     AddVehicleAdviceNewsItem(message, v->index);
    00977   }
    00978
    00979   cur_company.Restore();
    00980 }
    00981
    00986 static void DoDrawVehicle(const Vehicle *v)
    00987 {
    00988   SpriteID image = v->cur_image;
    00989   PaletteID pal = PAL_NONE;
    00990
    00991   if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
    00992
    00993   /* Check whether the vehicle shall be transparent due to the game state */
    00994   bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
    00995
    00996   if (v->type == VEH_EFFECT) {
    00997     /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
    00998      * However, transparent smoke and bubbles look weird, so always hide them. */
    00999     TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
    01000     if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
    01001   }
    01002
    01003   AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
    01004     v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
    01005 }
    01006
    01011 void ViewportAddVehicles(DrawPixelInfo *dpi)
    01012 {
    01013   /* The bounding rectangle */
    01014   const int l = dpi->left;
    01015   const int r = dpi->left + dpi->width;
    01016   const int t = dpi->top;
    01017   const int b = dpi->top + dpi->height;
    01018
    01019   /* The hash area to scan */
    01020   int xl, xu, yl, yu;
    01021
    01022   if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
    01023     xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
    01024     xu = GB(r,                        7 + ZOOM_LVL_SHIFT, 6);
    01025   } else {
    01026     /* scan whole hash row */
    01027     xl = 0;
    01028     xu = 0x3F;
    01029   }
    01030
    01031   if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
    01032     yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
    01033     yu = GB(b,                        6 + ZOOM_LVL_SHIFT, 6) << 6;
    01034   } else {
    01035     /* scan whole column */
    01036     yl = 0;
    01037     yu = 0x3F << 6;
    01038   }
    01039
    01040   for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
    01041     for (int x = xl;; x = (x + 1) & 0x3F) {
    01042       const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
    01043
    01044       while (v != NULL) {
    01045         if (!(v->vehstatus & VS_HIDDEN) &&
    01046             l <= v->coord.right &&
    01047             t <= v->coord.bottom &&
    01048             r >= v->coord.left &&
    01049             b >= v->coord.top) {
    01050           DoDrawVehicle(v);
    01051         }
    01052         v = v->hash_viewport_next;
    01053       }
    01054
    01055       if (x == xu) break;
    01056     }
    01057
    01058     if (y == yu) break;
    01059   }
    01060 }
    01061
    01069 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
    01070 {
    01071   Vehicle *found = NULL, *v;
    01072   uint dist, best_dist = UINT_MAX;
    01073
    01074   if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
    01075
    01076   x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
    01077   y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
    01078
    01079   FOR_ALL_VEHICLES(v) {
    01080     if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
    01081         x >= v->coord.left && x <= v->coord.right &&
    01082         y >= v->coord.top && y <= v->coord.bottom) {
    01083
    01084       dist = max(
    01085         abs(((v->coord.left + v->coord.right) >> 1) - x),
    01086         abs(((v->coord.top + v->coord.bottom) >> 1) - y)
    01087       );
    01088
    01089       if (dist < best_dist) {
    01090         found = v;
    01091         best_dist = dist;
    01092       }
    01093     }
    01094   }
    01095
    01096   return found;
    01097 }
    01098
    01103 void DecreaseVehicleValue(Vehicle *v)
    01104 {
    01105   v->value -= v->value >> 8;
    01106   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    01107 }
    01108
    01109 static const byte _breakdown_chance[64] = {
    01110     3,   3,   3,   3,   3,   3,   3,   3,
    01111     4,   4,   5,   5,   6,   6,   7,   7,
    01112     8,   8,   9,   9,  10,  10,  11,  11,
    01113    12,  13,  13,  13,  13,  14,  15,  16,
    01114    17,  19,  21,  25,  28,  31,  34,  37,
    01115    40,  44,  48,  52,  56,  60,  64,  68,
    01116    72,  80,  90, 100, 110, 120, 130, 140,
    01117   150, 170, 190, 210, 230, 250, 250, 250,
    01118 };
    01119
    01120 void CheckVehicleBreakdown(Vehicle *v)
    01121 {
    01122   int rel, rel_old;
    01123
    01124   /* decrease reliability */
    01125   v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
    01126   if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    01127
    01128   if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
    01129       _settings_game.difficulty.vehicle_breakdowns < 1 ||
    01130       v->cur_speed < 5 || _game_mode == GM_MENU) {
    01131     return;
    01132   }
    01133
    01134   uint32 r = Random();
    01135
    01136   /* increase chance of failure */
    01137   int chance = v->breakdown_chance + 1;
    01138   if (Chance16I(1, 25, r)) chance += 25;
    01139   v->breakdown_chance = min(255, chance);
    01140
    01141   /* calculate reliability value to use in comparison */
    01142   rel = v->reliability;
    01143   if (v->type == VEH_SHIP) rel += 0x6666;
    01144
    01145   /* reduced breakdowns? */
    01146   if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
    01147
    01148   /* check if to break down */
    01149   if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
    01150     v->breakdown_ctr    = GB(r, 16, 6) + 0x3F;
    01151     v->breakdown_delay  = GB(r, 24, 7) + 0x80;
    01152     v->breakdown_chance = 0;
    01153   }
    01154 }
    01155
    01162 bool Vehicle::HandleBreakdown()
    01163 {
    01164   /* Possible states for Vehicle::breakdown_ctr
    01165    * 0  - vehicle is running normally
    01166    * 1  - vehicle is currently broken down
    01167    * 2  - vehicle is going to break down now
    01168    * >2 - vehicle is counting down to the actual breakdown event */
    01169   switch (this->breakdown_ctr) {
    01170     case 0:
    01171       return false;
    01172
    01173     case 2:
    01174       this->breakdown_ctr = 1;
    01175
    01176       if (this->breakdowns_since_last_service != 255) {
    01177         this->breakdowns_since_last_service++;
    01178       }
    01179
    01180       if (this->type == VEH_AIRCRAFT) {
    01181         /* Aircraft just need this flag, the rest is handled elsewhere */
    01182         this->vehstatus |= VS_AIRCRAFT_BROKEN;
    01183       } else {
    01184         this->cur_speed = 0;
    01185
    01186         if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
    01187           SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
    01188             (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
    01189             (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
    01190         }
    01191
    01192         if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
    01193           EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
    01194           if (u != NULL) u->animation_state = this->breakdown_delay * 2;
    01195         }
    01196       }
    01197
    01198       this->MarkDirty(); // Update graphics after speed is zeroed
    01199       SetWindowDirty(WC_VEHICLE_VIEW, this->index);
    01200       SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
    01201
    01202       /* FALL THROUGH */
    01203     case 1:
    01204       /* Aircraft breakdowns end only when arriving at the airport */
    01205       if (this->type == VEH_AIRCRAFT) return false;
    01206
    01207       /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
    01208       if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
    01209         if (--this->breakdown_delay == 0) {
    01210           this->breakdown_ctr = 0;
    01211           this->MarkDirty();
    01212           SetWindowDirty(WC_VEHICLE_VIEW, this->index);
    01213         }
    01214       }
    01215       return true;
    01216
    01217     default:
    01218       if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
    01219       return false;
    01220   }
    01221 }
    01222
    01227 void AgeVehicle(Vehicle *v)
    01228 {
    01229   if (v->age < MAX_DAY) {
    01230     v->age++;
    01231     if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
    01232   }
    01233
    01234   if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
    01235
    01236   int age = v->age - v->max_age;
    01237   if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
    01238       age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
    01239     v->reliability_spd_dec <<= 1;
    01240   }
    01241
    01242   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    01243
    01244   /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
    01245   if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
    01246
    01247   /* Don't warn if a renew is active */
    01248   if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
    01249
    01250   StringID str;
    01251   if (age == -DAYS_IN_LEAP_YEAR) {
    01252     str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
    01253   } else if (age == 0) {
    01254     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
    01255   } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
    01256     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
    01257   } else {
    01258     return;
    01259   }
    01260
    01261   SetDParam(0, v->index);
    01262   AddVehicleAdviceNewsItem(str, v->index);
    01263 }
    01264
    01271 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
    01272 {
    01273   int count = 0;
    01274   int max = 0;
    01275   int cars = 0;
    01276   int unloading = 0;
    01277   bool loading = false;
    01278
    01279   bool is_loading = front->current_order.IsType(OT_LOADING);
    01280
    01281   /* The station may be NULL when the (colour) string does not need to be set. */
    01282   const Station *st = Station::GetIfValid(front->last_station_visited);
    01283   assert(colour == NULL || (st != NULL && is_loading));
    01284
    01285   bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
    01286   bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
    01287
    01288   /* Count up max and used */
    01289   for (const Vehicle *v = front; v != NULL; v = v->Next()) {
    01290     count += v->cargo.Count();
    01291     max += v->cargo_cap;
    01292     if (v->cargo_cap != 0 && colour != NULL) {
    01293       unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
    01294       loading |= !order_no_load &&
    01295           (order_full_load || HasBit(st->goods[v->cargo_type].acceptance_pickup, GoodsEntry::GES_PICKUP)) &&
    01296           !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
    01297       cars++;
    01298     }
    01299   }
    01300
    01301   if (colour != NULL) {
    01302     if (unloading == 0 && loading) {
    01303       *colour = STR_PERCENT_UP;
    01304     } else if (unloading == 0 && !loading) {
    01305       *colour = STR_PERCENT_NONE;
    01306     } else if (cars == unloading || !loading) {
    01307       *colour = STR_PERCENT_DOWN;
    01308     } else {
    01309       *colour = STR_PERCENT_UP_DOWN;
    01310     }
    01311   }
    01312
    01313   /* Train without capacity */
    01314   if (max == 0) return 100;
    01315
    01316   /* Return the percentage */
    01317   return (count * 100) / max;
    01318 }
    01319
    01324 void VehicleEnterDepot(Vehicle *v)
    01325 {
    01326   /* Always work with the front of the vehicle */
    01327   assert(v == v->First());
    01328
    01329   switch (v->type) {
    01330     case VEH_TRAIN: {
    01331       Train *t = Train::From(v);
    01332       SetWindowClassesDirty(WC_TRAINS_LIST);
    01333       /* Clear path reservation */
    01334       SetDepotReservation(t->tile, false);
    01335       if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
    01336
    01337       UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
    01338       t->wait_counter = 0;
    01339       t->force_proceed = TFP_NONE;
    01340       ClrBit(t->flags, VRF_TOGGLE_REVERSE);
    01341       t->ConsistChanged(true);
    01342       break;
    01343     }
    01344
    01345     case VEH_ROAD:
    01346       SetWindowClassesDirty(WC_ROADVEH_LIST);
    01347       break;
    01348
    01349     case VEH_SHIP: {
    01350       SetWindowClassesDirty(WC_SHIPS_LIST);
    01351       Ship *ship = Ship::From(v);
    01352       ship->state = TRACK_BIT_DEPOT;
    01353       ship->UpdateCache();
    01354       ship->UpdateViewport(true, true);
    01355       SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
    01356       break;
    01357     }
    01358
    01359     case VEH_AIRCRAFT:
    01360       SetWindowClassesDirty(WC_AIRCRAFT_LIST);
    01361       HandleAircraftEnterHangar(Aircraft::From(v));
    01362       break;
    01363     default: NOT_REACHED();
    01364   }
    01365   SetWindowDirty(WC_VEHICLE_VIEW, v->index);
    01366
    01367   if (v->type != VEH_TRAIN) {
    01368     /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
    01369      * 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 */
    01370     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    01371   }
    01372   SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
    01373
    01374   v->vehstatus |= VS_HIDDEN;
    01375   v->cur_speed = 0;
    01376
    01377   VehicleServiceInDepot(v);
    01378
    01379   /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
    01380   TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
    01381   v->MarkDirty();
    01382
    01383   if (v->current_order.IsType(OT_GOTO_DEPOT)) {
    01384     SetWindowDirty(WC_VEHICLE_VIEW, v->index);
    01385
    01386     const Order *real_order = v->GetOrder(v->cur_real_order_index);
    01387     Order t = v->current_order;
    01388     v->current_order.MakeDummy();
    01389
    01390     /* Test whether we are heading for this depot. If not, do nothing.
    01391      * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
    01392     if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
    01393         real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
    01394         (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
    01395       /* We are heading for another depot, keep driving. */
    01396       return;
    01397     }
    01398
    01399     if (t.IsRefit()) {
    01400       Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
    01401       CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
    01402       cur_company.Restore();
    01403
    01404       if (cost.Failed()) {
    01405         _vehicles_to_autoreplace[v] = false;
    01406         if (v->owner == _local_company) {
    01407           /* Notify the user that we stopped the vehicle */
    01408           SetDParam(0, v->index);
    01409           AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
    01410         }
    01411       } else if (cost.GetCost() != 0) {
    01412         v->profit_this_year -= cost.GetCost() << 8;
    01413         if (v->owner == _local_company) {
    01414           ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
    01415         }
    01416       }
    01417     }
    01418
    01419     if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
    01420       /* Part of orders */
    01421       v->DeleteUnreachedImplicitOrders();
    01422       UpdateVehicleTimetable(v, true);
    01423       v->IncrementImplicitOrderIndex();
    01424     }
    01425     if (t.GetDepotActionType() & ODATFB_HALT) {
    01426       /* Vehicles are always stopped on entering depots. Do not restart this one. */
    01427       _vehicles_to_autoreplace[v] = false;
    01428       if (v->owner == _local_company) {
    01429         SetDParam(0, v->index);
    01430         AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
    01431       }
    01432       AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
    01433     }
    01434   }
    01435 }
    01436
    01437
    01443 void VehicleUpdatePosition(Vehicle *v)
    01444 {
    01445   UpdateVehicleTileHash(v, false);
    01446 }
    01447
    01454 void VehicleUpdateViewport(Vehicle *v, bool dirty)
    01455 {
    01456   int img = v->cur_image;
    01457   Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
    01458   const Sprite *spr = GetSprite(img, ST_NORMAL);
    01459
    01460   pt.x += spr->x_offs;
    01461   pt.y += spr->y_offs;
    01462
    01463   UpdateVehicleViewportHash(v, pt.x, pt.y);
    01464
    01465   Rect old_coord = v->coord;
    01466   v->coord.left   = pt.x;
    01467   v->coord.top    = pt.y;
    01468   v->coord.right  = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
    01469   v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
    01470
    01471   if (dirty) {
    01472     if (old_coord.left == INVALID_COORD) {
    01473       MarkSingleVehicleDirty(v);
    01474     } else {
    01475       MarkAllViewportsDirty(
    01476         min(old_coord.left,   v->coord.left),
    01477         min(old_coord.top,    v->coord.top),
    01478         max(old_coord.right,  v->coord.right) + 1 * ZOOM_LVL_BASE,
    01479         max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
    01480       );
    01481     }
    01482   }
    01483 }
    01484
    01489 void VehicleUpdatePositionAndViewport(Vehicle *v)
    01490 {
    01491   VehicleUpdatePosition(v);
    01492   VehicleUpdateViewport(v, true);
    01493 }
    01494
    01499 void MarkSingleVehicleDirty(const Vehicle *v)
    01500 {
    01501   MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
    01502 }
    01503
    01509 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
    01510 {
    01511   static const int8 _delta_coord[16] = {
    01512     -1,-1,-1, 0, 1, 1, 1, 0, /* x */
    01513     -1, 0, 1, 1, 1, 0,-1,-1, /* y */
    01514   };
    01515
    01516   int x = v->x_pos + _delta_coord[v->direction];
    01517   int y = v->y_pos + _delta_coord[v->direction + 8];
    01518
    01519   GetNewVehiclePosResult gp;
    01520   gp.x = x;
    01521   gp.y = y;
    01522   gp.old_tile = v->tile;
    01523   gp.new_tile = TileVirtXY(x, y);
    01524   return gp;
    01525 }
    01526
    01527 static const Direction _new_direction_table[] = {
    01528   DIR_N,  DIR_NW, DIR_W,
    01529   DIR_NE, DIR_SE, DIR_SW,
    01530   DIR_E,  DIR_SE, DIR_S
    01531 };
    01532
    01533 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
    01534 {
    01535   int i = 0;
    01536
    01537   if (y >= v->y_pos) {
    01538     if (y != v->y_pos) i += 3;
    01539     i += 3;
    01540   }
    01541
    01542   if (x >= v->x_pos) {
    01543     if (x != v->x_pos) i++;
    01544     i++;
    01545   }
    01546
    01547   Direction dir = v->direction;
    01548
    01549   DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
    01550   if (dirdiff == DIRDIFF_SAME) return dir;
    01551   return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
    01552 }
    01553
    01563 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
    01564 {
    01565   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
    01566 }
    01567
    01575 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
    01576 {
    01577   /* Find maximum */
    01578   const Vehicle *v;
    01579   FOR_ALL_VEHICLES(v) {
    01580     if (v->type == type && v->owner == owner) {
    01581       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
    01582     }
    01583   }
    01584
    01585   if (this->maxid == 0) return;
    01586
    01587   /* Reserving 'maxid + 2' because we need:
    01588    * - space for the last item (with v->unitnumber == maxid)
    01589    * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
    01590   this->cache = CallocT<bool>(this->maxid + 2);
    01591
    01592   /* Fill the cache */
    01593   FOR_ALL_VEHICLES(v) {
    01594     if (v->type == type && v->owner == owner) {
    01595       this->cache[v->unitnumber] = true;
    01596     }
    01597   }
    01598 }
    01599
    01601 UnitID FreeUnitIDGenerator::NextID()
    01602 {
    01603   if (this->maxid <= this->curid) return ++this->curid;
    01604
    01605   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
    01606
    01607   return this->curid;
    01608 }
    01609
    01615 UnitID GetFreeUnitNumber(VehicleType type)
    01616 {
    01617   /* Check whether it is allowed to build another vehicle. */
    01618   uint max_veh;
    01619   switch (type) {
    01620     case VEH_TRAIN:    max_veh = _settings_game.vehicle.max_trains;   break;
    01621     case VEH_ROAD:     max_veh = _settings_game.vehicle.max_roadveh;  break;
    01622     case VEH_SHIP:     max_veh = _settings_game.vehicle.max_ships;    break;
    01623     case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
    01624     default: NOT_REACHED();
    01625   }
    01626
    01627   const Company *c = Company::Get(_current_company);
    01628   if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
    01629
    01630   FreeUnitIDGenerator gen(type, _current_company);
    01631
    01632   return gen.NextID();
    01633 }
    01634
    01635
    01644 bool CanBuildVehicleInfrastructure(VehicleType type)
    01645 {
    01646   assert(IsCompanyBuildableVehicleType(type));
    01647
    01648   if (!Company::IsValidID(_local_company)) return false;
    01649   if (!_settings_client.gui.disable_unsuitable_building) return true;
    01650
    01651   UnitID max;
    01652   switch (type) {
    01653     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
    01654     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
    01655     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
    01656     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
    01657     default: NOT_REACHED();
    01658   }
    01659
    01660   /* We can build vehicle infrastructure when we may build the vehicle type */
    01661   if (max > 0) {
    01662     /* Can we actually build the vehicle type? */
    01663     const Engine *e;
    01664     FOR_ALL_ENGINES_OF_TYPE(e, type) {
    01665       if (HasBit(e->company_avail, _local_company)) return true;
    01666     }
    01667     return false;
    01668   }
    01669
    01670   /* We should be able to build infrastructure when we have the actual vehicle type */
    01671   const Vehicle *v;
    01672   FOR_ALL_VEHICLES(v) {
    01673     if (v->owner == _local_company && v->type == type) return true;
    01674   }
    01675
    01676   return false;
    01677 }
    01678
    01679
    01687 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
    01688 {
    01689   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
    01690   const Engine *e = Engine::Get(engine_type);
    01691   switch (e->type) {
    01692     default: NOT_REACHED();
    01693     case VEH_TRAIN:
    01694       if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
    01695         /* Wagonoverrides use the colour scheme of the front engine.
    01696          * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
    01697         engine_type = parent_engine_type;
    01698         e = Engine::Get(engine_type);
    01699         /* Note: Luckily cargo_type is not needed for engines */
    01700       }
    01701
    01702       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01703       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01704       if (e->u.rail.railveh_type == RAILVEH_WAGON) {
    01705         if (!CargoSpec::Get(cargo_type)->is_freight) {
    01706           if (parent_engine_type == INVALID_ENGINE) {
    01707             return LS_PASSENGER_WAGON_STEAM;
    01708           } else {
    01709             switch (RailVehInfo(parent_engine_type)->engclass) {
    01710               default: NOT_REACHED();
    01711               case EC_STEAM:    return LS_PASSENGER_WAGON_STEAM;
    01712               case EC_DIESEL:   return LS_PASSENGER_WAGON_DIESEL;
    01713               case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
    01714               case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
    01715               case EC_MAGLEV:   return LS_PASSENGER_WAGON_MAGLEV;
    01716             }
    01717           }
    01718         } else {
    01719           return LS_FREIGHT_WAGON;
    01720         }
    01721       } else {
    01722         bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
    01723
    01724         switch (e->u.rail.engclass) {
    01725           default: NOT_REACHED();
    01726           case EC_STEAM:    return LS_STEAM;
    01727           case EC_DIESEL:   return is_mu ? LS_DMU : LS_DIESEL;
    01728           case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
    01729           case EC_MONORAIL: return LS_MONORAIL;
    01730           case EC_MAGLEV:   return LS_MAGLEV;
    01731         }
    01732       }
    01733
    01734     case VEH_ROAD:
    01735       /* Always use the livery of the front */
    01736       if (v != NULL && parent_engine_type != INVALID_ENGINE) {
    01737         engine_type = parent_engine_type;
    01738         e = Engine::Get(engine_type);
    01739         cargo_type = v->First()->cargo_type;
    01740       }
    01741       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01742       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01743
    01744       /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
    01745       if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
    01746         /* Tram */
    01747         return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
    01748       } else {
    01749         /* Bus or truck */
    01750         return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
    01751       }
    01752
    01753     case VEH_SHIP:
    01754       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
    01755       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
    01756       return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
    01757
    01758     case VEH_AIRCRAFT:
    01759       switch (e->u.air.subtype) {
    01760         case AIR_HELI: return LS_HELICOPTER;
    01761         case AIR_CTOL: return LS_SMALL_PLANE;
    01762         case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
    01763         default: NOT_REACHED();
    01764       }
    01765   }
    01766 }
    01767
    01777 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
    01778 {
    01779   const Company *c = Company::Get(company);
    01780   LiveryScheme scheme = LS_DEFAULT;
    01781
    01782   /* The default livery is always available for use, but its in_use flag determines
    01783    * whether any _other_ liveries are in use. */
    01784   if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
    01785     /* Determine the livery scheme to use */
    01786     scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
    01787
    01788     /* Switch back to the default scheme if the resolved scheme is not in use */
    01789     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
    01790   }
    01791
    01792   return &c->livery[scheme];
    01793 }
    01794
    01795
    01796 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
    01797 {
    01798   PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
    01799
    01800   /* Return cached value if any */
    01801   if (map != PAL_NONE) return map;
    01802
    01803   const Engine *e = Engine::Get(engine_type);
    01804
    01805   /* Check if we should use the colour map callback */
    01806   if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
    01807     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
    01808     /* Failure means "use the default two-colour" */
    01809     if (callback != CALLBACK_FAILED) {
    01810       assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
    01811       map = GB(callback, 0, 14);
    01812       /* If bit 14 is set, then the company colours are applied to the
    01813        * map else it's returned as-is. */
    01814       if (!HasBit(callback, 14)) {
    01815         /* Update cache */
    01816         if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
    01817         return map;
    01818       }
    01819     }
    01820   }
    01821
    01822   bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
    01823
    01824   if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
    01825
    01826   /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
    01827   if (!Company::IsValidID(company)) return map;
    01828
    01829   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
    01830
    01831   map += livery->colour1;
    01832   if (twocc) map += livery->colour2 * 16;
    01833
    01834   /* Update cache */
    01835   if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
    01836   return map;
    01837 }
    01838
    01845 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
    01846 {
    01847   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
    01848 }
    01849
    01855 PaletteID GetVehiclePalette(const Vehicle *v)
    01856 {
    01857   if (v->IsGroundVehicle()) {
    01858     return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
    01859   }
    01860
    01861   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
    01862 }
    01863
    01867 void Vehicle::DeleteUnreachedImplicitOrders()
    01868 {
    01869   if (this->IsGroundVehicle()) {
    01870     uint16 &gv_flags = this->GetGroundVehicleFlags();
    01871     if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
    01872       /* Do not delete orders, only skip them */
    01873       ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
    01874       this->cur_implicit_order_index = this->cur_real_order_index;
    01875       InvalidateVehicleOrder(this, 0);
    01876       return;
    01877     }
    01878   }
    01879
    01880   const Order *order = this->GetOrder(this->cur_implicit_order_index);
    01881   while (order != NULL) {
    01882     if (this->cur_implicit_order_index == this->cur_real_order_index) break;
    01883
    01884     if (order->IsType(OT_IMPLICIT)) {
    01885       DeleteOrder(this, this->cur_implicit_order_index);
    01886       /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
    01887       order = this->GetOrder(this->cur_implicit_order_index);
    01888     } else {
    01889       /* Skip non-implicit orders, e.g. service-orders */
    01890       order = order->next;
    01891       this->cur_implicit_order_index++;
    01892     }
    01893
    01894     /* Wrap around */
    01895     if (order == NULL) {
    01896       order = this->GetOrder(0);
    01897       this->cur_implicit_order_index = 0;
    01898     }
    01899   }
    01900 }
    01901
    01906 void Vehicle::BeginLoading()
    01907 {
    01908   assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
    01909
    01910   if (this->current_order.IsType(OT_GOTO_STATION) &&
    01911       this->current_order.GetDestination() == this->last_station_visited) {
    01912     this->DeleteUnreachedImplicitOrders();
    01913
    01914     /* Now both order indices point to the destination station, and we can start loading */
    01915     this->current_order.MakeLoading(true);
    01916     UpdateVehicleTimetable(this, true);
    01917
    01918     /* Furthermore add the Non Stop flag to mark that this station
    01919      * is the actual destination of the vehicle, which is (for example)
    01920      * necessary to be known for HandleTrainLoading to determine
    01921      * whether the train is lost or not; not marking a train lost
    01922      * that arrives at random stations is bad. */
    01923     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
    01924
    01925   } else {
    01926     /* We weren't scheduled to stop here. Insert an implicit order
    01927      * to show that we are stopping here, but only do that if the order
    01928      * list isn't empty.
    01929      * While only groundvehicles have implicit orders, e.g. aircraft might still enter
    01930      * the 'wrong' terminal when skipping orders etc. */
    01931     Order *in_list = this->GetOrder(this->cur_implicit_order_index);
    01932     if (this->IsGroundVehicle() && in_list != NULL &&
    01933         (!in_list->IsType(OT_IMPLICIT) ||
    01934         in_list->GetDestination() != this->last_station_visited)) {
    01935       bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
    01936       /* Do not create consecutive duplicates of implicit orders */
    01937       Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
    01938       if (prev_order == NULL ||
    01939           (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
    01940           prev_order->GetDestination() != this->last_station_visited) {
    01941
    01942         /* Prefer deleting implicit orders instead of inserting new ones,
    01943          * so test whether the right order follows later */
    01944         int target_index = this->cur_implicit_order_index;
    01945         bool found = false;
    01946         while (target_index != this->cur_real_order_index) {
    01947           const Order *order = this->GetOrder(target_index);
    01948           if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
    01949             found = true;
    01950             break;
    01951           }
    01952           target_index++;
    01953           if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
    01954           assert(target_index != this->cur_implicit_order_index); // infinite loop?
    01955         }
    01956
    01957         if (found) {
    01958           if (suppress_implicit_orders) {
    01959             /* Skip to the found order */
    01960             this->cur_implicit_order_index = target_index;
    01961             InvalidateVehicleOrder(this, 0);
    01962           } else {
    01963             /* Delete all implicit orders up to the station we just reached */
    01964             const Order *order = this->GetOrder(this->cur_implicit_order_index);
    01965             while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
    01966               if (order->IsType(OT_IMPLICIT)) {
    01967                 DeleteOrder(this, this->cur_implicit_order_index);
    01968                 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
    01969                 order = this->GetOrder(this->cur_implicit_order_index);
    01970               } else {
    01971                 /* Skip non-implicit orders, e.g. service-orders */
    01972                 order = order->next;
    01973                 this->cur_implicit_order_index++;
    01974               }
    01975
    01976               /* Wrap around */
    01977               if (order == NULL) {
    01978                 order = this->GetOrder(0);
    01979                 this->cur_implicit_order_index = 0;
    01980               }
    01981               assert(order != NULL);
    01982             }
    01983           }
    01984         } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
    01985           /* Insert new implicit order */
    01986           Order *implicit_order = new Order();
    01987           implicit_order->MakeImplicit(this->last_station_visited);
    01988           InsertOrder(this, implicit_order, this->cur_implicit_order_index);
    01989           if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
    01990
    01991           /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
    01992            * Reenable it for this vehicle */
    01993           uint16 &gv_flags = this->GetGroundVehicleFlags();
    01994           ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
    01995         }
    01996       }
    01997     }
    01998     this->current_order.MakeLoading(false);
    01999   }
    02000
    02001   Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
    02002
    02003   PrepareUnload(this);
    02004
    02005   SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
    02006   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
    02007   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
    02008   SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
    02009
    02010   Station::Get(this->last_station_visited)->MarkTilesDirty(true);
    02011   this->cur_speed = 0;
    02012   this->MarkDirty();
    02013 }
    02014
    02019 void Vehicle::LeaveStation()
    02020 {
    02021   assert(this->current_order.IsType(OT_LOADING));
    02022
    02023   delete this->cargo_payment;
    02024
    02025   /* Only update the timetable if the vehicle was supposed to stop here. */
    02026   if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
    02027
    02028   this->current_order.MakeLeaveStation();
    02029   Station *st = Station::Get(this->last_station_visited);
    02030   st->loading_vehicles.remove(this);
    02031
    02032   HideFillingPercent(&this->fill_percent_te_id);
    02033
    02034   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
    02035     /* Trigger station animation (trains only) */
    02036     if (IsTileType(this->tile, MP_STATION)) {
    02037       TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
    02038       TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
    02039     }
    02040
    02041     SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
    02042   }
    02043 }
    02044
    02045
    02051 void Vehicle::HandleLoading(bool mode)
    02052 {
    02053   switch (this->current_order.GetType()) {
    02054     case OT_LOADING: {
    02055       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
    02056
    02057       /* Not the first call for this tick, or still loading */
    02058       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
    02059
    02060       this->PlayLeaveStationSound();
    02061
    02062       this->LeaveStation();
    02063
    02064       /* Only advance to next order if we just loaded at the current one */
    02065       const Order *order = this->GetOrder(this->cur_implicit_order_index);
    02066       if (order == NULL ||
    02067           (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
    02068           order->GetDestination() != this->last_station_visited) {
    02069         return;
    02070       }
    02071       break;
    02072     }
    02073
    02074     case OT_DUMMY: break;
    02075
    02076     default: return;
    02077   }
    02078
    02079   this->IncrementImplicitOrderIndex();
    02080 }
    02081
    02088 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
    02089 {
    02090   CommandCost ret = CheckOwnership(this->owner);
    02091   if (ret.Failed()) return ret;
    02092
    02093   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
    02094   if (this->IsStoppedInDepot()) return CMD_ERROR;
    02095
    02096   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
    02097     bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
    02098     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
    02099       /* We called with a different DEPOT_SERVICE setting.
    02100        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
    02101        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
    02102       if (flags & DC_EXEC) {
    02103         this->current_order.SetDepotOrderType(ODTF_MANUAL);
    02104         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
    02105         SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
    02106       }
    02107       return CommandCost();
    02108     }
    02109
    02110     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
    02111     if (flags & DC_EXEC) {
    02112       /* If the orders to 'goto depot' are in the orders list (forced servicing),
    02113        * then skip to the next order; effectively cancelling this forced service */
    02114       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
    02115
    02116       if (this->IsGroundVehicle()) {
    02117         uint16 &gv_flags = this->GetGroundVehicleFlags();
    02118         SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
    02119       }
    02120
    02121       this->current_order.MakeDummy();
    02122       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
    02123     }
    02124     return CommandCost();
    02125   }
    02126
    02127   TileIndex location;
    02128   DestinationID destination;
    02129   bool reverse;
    02130   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};
    02131   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
    02132
    02133   if (flags & DC_EXEC) {
    02134     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
    02135
    02136     if (this->IsGroundVehicle()) {
    02137       uint16 &gv_flags = this->GetGroundVehicleFlags();
    02138       SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
    02139     }
    02140
    02141     this->dest_tile = location;
    02142     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
    02143     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
    02144     SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
    02145
    02146     /* If there is no depot in front, reverse automatically (trains only) */
    02147     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
    02148
    02149     if (this->type == VEH_AIRCRAFT) {
    02150       Aircraft *a = Aircraft::From(this);
    02151       if (a->state == FLYING && a->targetairport != destination) {
    02152         /* The aircraft is now heading for a different hangar than the next in the orders */
    02153         extern void AircraftNextAirportPos_and_Order(Aircraft *a);
    02154         AircraftNextAirportPos_and_Order(a);
    02155       }
    02156     }
    02157   }
    02158
    02159   return CommandCost();
    02160
    02161 }
    02162
    02167 void Vehicle::UpdateVisualEffect(bool allow_power_change)
    02168 {
    02169   bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
    02170   const Engine *e = this->GetEngine();
    02171
    02172   /* Evaluate properties */
    02173   byte visual_effect;
    02174   switch (e->type) {
    02175     case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
    02176     case VEH_ROAD:  visual_effect = e->u.road.visual_effect; break;
    02177     case VEH_SHIP:  visual_effect = e->u.ship.visual_effect; break;
    02178     default:        visual_effect = 1 << VE_DISABLE_EFFECT;  break;
    02179   }
    02180
    02181   /* Check powered wagon / visual effect callback */
    02182   if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
    02183     uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
    02184
    02185     if (callback != CALLBACK_FAILED) {
    02186       if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
    02187
    02188       callback = GB(callback, 0, 8);
    02189       /* Avoid accidentally setting 'visual_effect' to the default value
    02190        * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
    02191       if (callback == VE_DEFAULT) {
    02192         assert(HasBit(callback, VE_DISABLE_EFFECT));
    02193         SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
    02194       }
    02195       visual_effect = callback;
    02196     }
    02197   }
    02198
    02199   /* Apply default values */
    02200   if (visual_effect == VE_DEFAULT ||
    02201       (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
    02202     /* Only train engines have default effects.
    02203      * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
    02204     if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
    02205       if (visual_effect == VE_DEFAULT) {
    02206         visual_effect = 1 << VE_DISABLE_EFFECT;
    02207       } else {
    02208         SetBit(visual_effect, VE_DISABLE_EFFECT);
    02209       }
    02210     } else {
    02211       if (visual_effect == VE_DEFAULT) {
    02212         /* Also set the offset */
    02213         visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
    02214       }
    02215       SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
    02216     }
    02217   }
    02218
    02219   this->vcache.cached_vis_effect = visual_effect;
    02220
    02221   if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
    02222     ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
    02223     ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
    02224   }
    02225 }
    02226
    02227 static const int8 _vehicle_smoke_pos[8] = {
    02228   1, 1, 1, 0, -1, -1, -1, 0
    02229 };
    02230
    02235 void Vehicle::ShowVisualEffect() const
    02236 {
    02237   assert(this->IsPrimaryVehicle());
    02238   bool sound = false;
    02239
    02240   /* Do not show any smoke when:
    02241    * - vehicle smoke is disabled by the player
    02242    * - the vehicle is slowing down or stopped (by the player)
    02243    * - the vehicle is moving very slowly
    02244    */
    02245   if (_settings_game.vehicle.smoke_amount == 0 ||
    02246       this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
    02247       this->cur_speed < 2) {
    02248     return;
    02249   }
    02250
    02251   uint max_speed = this->vcache.cached_max_speed;
    02252   if (this->type == VEH_TRAIN) {
    02253     const Train *t = Train::From(this);
    02254     /* For trains, do not show any smoke when:
    02255      * - the train is reversing
    02256      * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
    02257      */
    02258     if (HasBit(t->flags, VRF_REVERSING) ||
    02259         (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
    02260         t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
    02261       return;
    02262     }
    02263
    02264     max_speed = min(max_speed, t->gcache.cached_max_track_speed);
    02265     max_speed = min(max_speed, this->current_order.max_speed);
    02266   }
    02267   if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
    02268
    02269   const Vehicle *v = this;
    02270
    02271   do {
    02272     int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
    02273     byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
    02274     bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
    02275
    02276     /* Show no smoke when:
    02277      * - Smoke has been disabled for this vehicle
    02278      * - The vehicle is not visible
    02279      * - The vehicle is under a bridge
    02280      * - The vehicle is on a depot tile
    02281      * - The vehicle is on a tunnel tile
    02282      * - The vehicle is a train engine that is currently unpowered */
    02283     if (disable_effect ||
    02284         v->vehstatus & VS_HIDDEN ||
    02285         (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
    02286         IsDepotTile(v->tile) ||
    02287         IsTunnelTile(v->tile) ||
    02288         (v->type == VEH_TRAIN &&
    02289         !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
    02290       continue;
    02291     }
    02292
    02293     /* The effect offset is relative to a point 4 units behind the vehicle's
    02294      * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
    02295      * correction factor. */
    02296     if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
    02297
    02298     int x = _vehicle_smoke_pos[v->direction] * effect_offset;
    02299     int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
    02300
    02301     if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
    02302       x = -x;
    02303       y = -y;
    02304     }
    02305
    02306     switch (effect_type) {
    02307       case VE_TYPE_STEAM:
    02308         /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
    02309          * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
    02310          * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
    02311          * REGULATION:
    02312          * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
    02313         if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
    02314           CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
    02315           sound = true;
    02316         }
    02317         break;
    02318
    02319       case VE_TYPE_DIESEL: {
    02320         /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
    02321          * when smoke emission stops.
    02322          * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
    02323          * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
    02324          * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
    02325          * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
    02326          * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
    02327          * maximum speed no diesel_smoke is emitted.
    02328          * REGULATION:
    02329          * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
    02330          * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
    02331         int power_weight_effect = 0;
    02332         if (v->type == VEH_TRAIN) {
    02333           power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
    02334         }
    02335         if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
    02336             Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
    02337           CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
    02338           sound = true;
    02339         }
    02340         break;
    02341       }
    02342
    02343       case VE_TYPE_ELECTRIC:
    02344         /* Electric train's spark - more often occurs when train is departing (more load)
    02345          * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
    02346          * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
    02347          * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
    02348          * REGULATION:
    02349          * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
    02350         if (GB(v->tick_counter, 0, 2) == 0 &&
    02351             Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
    02352           CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
    02353           sound = true;
    02354         }
    02355         break;
    02356
    02357       default:
    02358         break;
    02359     }
    02360   } while ((v = v->Next()) != NULL);
    02361
    02362   if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
    02363 }
    02364
    02369 void Vehicle::SetNext(Vehicle *next)
    02370 {
    02371   assert(this != next);
    02372
    02373   if (this->next != NULL) {
    02374     /* We had an old next vehicle. Update the first and previous pointers */
    02375     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    02376       v->first = this->next;
    02377     }
    02378     this->next->previous = NULL;
    02379   }
    02380
    02381   this->next = next;
    02382
    02383   if (this->next != NULL) {
    02384     /* A new next vehicle. Update the first and previous pointers */
    02385     if (this->next->previous != NULL) this->next->previous->next = NULL;
    02386     this->next->previous = this;
    02387     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
    02388       v->first = this->first;
    02389     }
    02390   }
    02391 }
    02392
    02398 void Vehicle::AddToShared(Vehicle *shared_chain)
    02399 {
    02400   assert(this->previous_shared == NULL && this->next_shared == NULL);
    02401
    02402   if (shared_chain->orders.list == NULL) {
    02403     assert(shared_chain->previous_shared == NULL);
    02404     assert(shared_chain->next_shared == NULL);
    02405     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
    02406   }
    02407
    02408   this->next_shared     = shared_chain->next_shared;
    02409   this->previous_shared = shared_chain;
    02410
    02411   shared_chain->next_shared = this;
    02412
    02413   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
    02414
    02415   shared_chain->orders.list->AddVehicle(this);
    02416 }
    02417
    02421 void Vehicle::RemoveFromShared()
    02422 {
    02423   /* Remember if we were first and the old window number before RemoveVehicle()
    02424    * as this changes first if needed. */
    02425   bool were_first = (this->FirstShared() == this);
    02426   VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
    02427
    02428   this->orders.list->RemoveVehicle(this);
    02429
    02430   if (!were_first) {
    02431     /* We are not the first shared one, so only relink our previous one. */
    02432     this->previous_shared->next_shared = this->NextShared();
    02433   }
    02434
    02435   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
    02436
    02437
    02438   if (this->orders.list->GetNumVehicles() == 1) {
    02439     /* When there is only one vehicle, remove the shared order list window. */
    02440     DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
    02441     InvalidateVehicleOrder(this->FirstShared(), 0);
    02442   } else if (were_first) {
    02443     /* If we were the first one, update to the new first one.
    02444      * Note: FirstShared() is already the new first */
    02445     InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
    02446   }
    02447
    02448   this->next_shared     = NULL;
    02449   this->previous_shared = NULL;
    02450 }
    02451
    02452 void VehiclesYearlyLoop()
    02453 {
    02454   Vehicle *v;
    02455   FOR_ALL_VEHICLES(v) {
    02456     if (v->IsPrimaryVehicle()) {
    02457       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
    02458       Money profit = v->GetDisplayProfitThisYear();
    02459       if (v->age >= 730 && profit < 0) {
    02460         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
    02461           SetDParam(0, v->index);
    02462           SetDParam(1, profit);
    02463           AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
    02464         }
    02465         AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
    02466       }
    02467
    02468       v->profit_last_year = v->profit_this_year;
    02469       v->profit_this_year = 0;
    02470       SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    02471     }
    02472   }
    02473   GroupStatistics::UpdateProfits();
    02474   SetWindowClassesDirty(WC_TRAINS_LIST);
    02475   SetWindowClassesDirty(WC_SHIPS_LIST);
    02476   SetWindowClassesDirty(WC_ROADVEH_LIST);
    02477   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
    02478 }
    02479
    02480
    02490 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
    02491 {
    02492   const Engine *e = Engine::GetIfValid(engine_type);
    02493   assert(e != NULL);
    02494
    02495   switch (e->type) {
    02496     case VEH_TRAIN:
    02497       return (st->facilities & FACIL_TRAIN) != 0;
    02498
    02499     case VEH_ROAD:
    02500       /* For road vehicles we need the vehicle to know whether it can actually
    02501        * use the station, but if it doesn't have facilities for RVs it is
    02502        * certainly not possible that the station can be used. */
    02503       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
    02504
    02505     case VEH_SHIP:
    02506       return (st->facilities & FACIL_DOCK) != 0;
    02507
    02508     case VEH_AIRCRAFT:
    02509       return (st->facilities & FACIL_AIRPORT) != 0 &&
    02510           (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
    02511
    02512     default:
    02513       return false;
    02514   }
    02515 }
    02516
    02523 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
    02524 {
    02525   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
    02526
    02527   return CanVehicleUseStation(v->engine_type, st);
    02528 }
    02529
    02535 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
    02536 {
    02537   assert(this->IsGroundVehicle());
    02538   if (this->type == VEH_TRAIN) {
    02539     return &Train::From(this)->gcache;
    02540   } else {
    02541     return &RoadVehicle::From(this)->gcache;
    02542   }
    02543 }
    02544
    02550 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
    02551 {
    02552   assert(this->IsGroundVehicle());
    02553   if (this->type == VEH_TRAIN) {
    02554     return &Train::From(this)->gcache;
    02555   } else {
    02556     return &RoadVehicle::From(this)->gcache;
    02557   }
    02558 }
    02559
    02565 uint16 &Vehicle::GetGroundVehicleFlags()
    02566 {
    02567   assert(this->IsGroundVehicle());
    02568   if (this->type == VEH_TRAIN) {
    02569     return Train::From(this)->gv_flags;
    02570   } else {
    02571     return RoadVehicle::From(this)->gv_flags;
    02572   }
    02573 }
    02574
    02580 const uint16 &Vehicle::GetGroundVehicleFlags() const
    02581 {
    02582   assert(this->IsGroundVehicle());
    02583   if (this->type == VEH_TRAIN) {
    02584     return Train::From(this)->gv_flags;
    02585   } else {
    02586     return RoadVehicle::From(this)->gv_flags;
    02587   }
    02588 }
    02589
    02598 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
    02599 {
    02600   if (v->type == VEH_TRAIN) {
    02601     Train *u = Train::From(v);
    02602     /* Only include whole vehicles, so start with the first articulated part */
    02603     u = u->GetFirstEnginePart();
    02604
    02605     /* Include num_vehicles vehicles, not counting articulated parts */
    02606     for (; u != NULL && num_vehicles > 0; num_vehicles--) {
    02607       do {
    02608         /* Include current vehicle in the selection. */
    02609         set.Include(u->index);
    02610
    02611         /* If the vehicle is multiheaded, add the other part too. */
    02612         if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
    02613
    02614         u = u->Next();
    02615       } while (u != NULL && u->IsArticulatedPart());
    02616     }
    02617   }
    02618 }