vehicle.cpp

Go to the documentation of this file.
00001 /* $Id: vehicle.cpp 21679 2010-12-31 15:53:46Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "gui.h"
00014 #include "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 "vehicle_gui.h"
00023 #include "train.h"
00024 #include "aircraft.h"
00025 #include "newgrf_debug.h"
00026 #include "newgrf_sound.h"
00027 #include "newgrf_station.h"
00028 #include "group.h"
00029 #include "group_gui.h"
00030 #include "strings_func.h"
00031 #include "zoom_func.h"
00032 #include "functions.h"
00033 #include "date_func.h"
00034 #include "window_func.h"
00035 #include "vehicle_func.h"
00036 #include "autoreplace_func.h"
00037 #include "autoreplace_gui.h"
00038 #include "station_base.h"
00039 #include "ai/ai.hpp"
00040 #include "depot_func.h"
00041 #include "network/network.h"
00042 #include "core/pool_func.hpp"
00043 #include "economy_base.h"
00044 #include "articulated_vehicles.h"
00045 #include "roadstop_base.h"
00046 #include "core/random_func.hpp"
00047 #include "core/backup_type.hpp"
00048 #include "order_backup.h"
00049 #include "sound_func.h"
00050 #include "effectvehicle_func.h"
00051 #include "effectvehicle_base.h"
00052 #include "vehiclelist.h"
00053 #include "tunnel_map.h"
00054 #include "depot_map.h"
00055 #include "ground_vehicle.hpp"
00056 
00057 #include "table/strings.h"
00058 
00059 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00060 
00061 VehicleID _vehicle_id_ctr_day;
00062 VehicleID _new_vehicle_id;
00063 uint16 _returned_refit_capacity;      
00064 uint16 _returned_mail_refit_capacity; 
00065 byte _age_cargo_skip_counter;         
00066 
00067 
00068 /* Initialize the vehicle-pool */
00069 VehiclePool _vehicle_pool("Vehicle");
00070 INSTANTIATE_POOL_METHODS(Vehicle)
00071 
00072 
00077 bool Vehicle::NeedsAutorenewing(const Company *c) const
00078 {
00079   /* We can always generate the Company pointer when we have the vehicle.
00080    * However this takes time and since the Company pointer is often present
00081    * when this function is called then it's faster to pass the pointer as an
00082    * argument rather than finding it again. */
00083   assert(c == Company::Get(this->owner));
00084 
00085   if (!c->settings.engine_renew) return false;
00086   if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00087   if (this->age == 0) return false; // rail cars don't age and lacks a max age
00088 
00089   return true;
00090 }
00091 
00092 void VehicleServiceInDepot(Vehicle *v)
00093 {
00094   v->date_of_last_service = _date;
00095   v->breakdowns_since_last_service = 0;
00096   v->reliability = Engine::Get(v->engine_type)->reliability;
00097   SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
00098 }
00099 
00100 bool Vehicle::NeedsServicing() const
00101 {
00102   /* Stopped or crashed vehicles will not move, as such making unmovable
00103    * vehicles to go for service is lame. */
00104   if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00105 
00106   /* Are we ready for the next service cycle? */
00107   const Company *c = Company::Get(this->owner);
00108   if (c->settings.vehicle.servint_ispercent ?
00109       (this->reliability >= Engine::Get(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00110       (this->date_of_last_service + this->service_interval >= _date)) {
00111     return false;
00112   }
00113 
00114   /* If we're servicing anyway, because we have not disabled servicing when
00115    * there are no breakdowns or we are playing with breakdowns, bail out. */
00116   if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00117       _settings_game.difficulty.vehicle_breakdowns != 0) {
00118     return true;
00119   }
00120 
00121   /* Test whether there is some pending autoreplace.
00122    * Note: We do this after the service-interval test.
00123    * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
00124   bool pending_replace = false;
00125   Money needed_money = c->settings.engine_renew_money;
00126   if (needed_money > c->money) return false;
00127 
00128   for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00129     EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00130 
00131     /* Check engine availability */
00132     if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00133 
00134     /* Check refittability */
00135     uint32 available_cargo_types, union_mask;
00136     GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00137     /* Is there anything to refit? */
00138     if (union_mask != 0) {
00139       CargoID cargo_type;
00140       /* We cannot refit to mixed cargoes in an automated way */
00141       if (IsArticulatedVehicleCarryingDifferentCargos(v, &cargo_type)) continue;
00142 
00143       /* Did the old vehicle carry anything? */
00144       if (cargo_type != CT_INVALID) {
00145         /* We can't refit the vehicle to carry the cargo we want */
00146         if (!HasBit(available_cargo_types, cargo_type)) continue;
00147       }
00148     }
00149 
00150     /* Check money.
00151      * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
00152     pending_replace = true;
00153     needed_money += 2 * Engine::Get(new_engine)->GetCost();
00154     if (needed_money > c->money) return false;
00155   }
00156 
00157   return pending_replace;
00158 }
00159 
00160 bool Vehicle::NeedsAutomaticServicing() const
00161 {
00162   if (_settings_game.order.gotodepot && this->HasDepotOrder()) return false;
00163   if (this->current_order.IsType(OT_LOADING)) return false;
00164   if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00165   return NeedsServicing();
00166 }
00167 
00168 uint Vehicle::Crash(bool flooded)
00169 {
00170   assert((this->vehstatus & VS_CRASHED) == 0);
00171   assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
00172 
00173   uint pass = 0;
00174   /* Stop the vehicle. */
00175   if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00176   /* crash all wagons, and count passengers */
00177   for (Vehicle *v = this; v != NULL; v = v->Next()) {
00178     if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00179     v->vehstatus |= VS_CRASHED;
00180     MarkSingleVehicleDirty(v);
00181   }
00182 
00183   /* Dirty some windows */
00184   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00185   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
00186   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00187   SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00188 
00189   return pass;
00190 }
00191 
00192 
00201 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00202 {
00203   const Engine *e = Engine::Get(engine);
00204   uint32 grfid = e->grf_prop.grffile->grfid;
00205   GRFConfig *grfconfig = GetGRFConfig(grfid);
00206 
00207   if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00208     SetBit(grfconfig->grf_bugs, bug_type);
00209     SetDParamStr(0, grfconfig->GetName());
00210     SetDParam(1, engine);
00211     ShowErrorMessage(part1, part2, WL_CRITICAL);
00212     if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00213   }
00214 
00215   /* debug output */
00216   char buffer[512];
00217 
00218   SetDParamStr(0, grfconfig->GetName());
00219   GetString(buffer, part1, lastof(buffer));
00220   DEBUG(grf, 0, "%s", buffer + 3);
00221 
00222   SetDParam(1, engine);
00223   GetString(buffer, part2, lastof(buffer));
00224   DEBUG(grf, 0, "%s", buffer + 3);
00225 }
00226 
00231 Vehicle::Vehicle(VehicleType type)
00232 {
00233   this->type               = type;
00234   this->coord.left         = INVALID_COORD;
00235   this->group_id           = DEFAULT_GROUP;
00236   this->fill_percent_te_id = INVALID_TE_ID;
00237   this->first              = this;
00238   this->colourmap          = PAL_NONE;
00239 }
00240 
00245 byte VehicleRandomBits()
00246 {
00247   return GB(Random(), 0, 8);
00248 }
00249 
00250 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
00251  * lookup times at the expense of memory usage. */
00252 const int HASH_BITS = 7;
00253 const int HASH_SIZE = 1 << HASH_BITS;
00254 const int HASH_MASK = HASH_SIZE - 1;
00255 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00256 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00257 
00258 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
00259  * Profiling results show that 0 is fastest. */
00260 const int HASH_RES = 0;
00261 
00262 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00263 
00264 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00265 {
00266   for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00267     for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00268       Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00269       for (; v != NULL; v = v->next_new_hash) {
00270         Vehicle *a = proc(v, data);
00271         if (find_first && a != NULL) return a;
00272       }
00273       if (x == xu) break;
00274     }
00275     if (y == yu) break;
00276   }
00277 
00278   return NULL;
00279 }
00280 
00281 
00293 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00294 {
00295   const int COLL_DIST = 6;
00296 
00297   /* Hash area to scan is from xl,yl to xu,yu */
00298   int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00299   int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00300   int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00301   int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00302 
00303   return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00304 }
00305 
00320 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00321 {
00322   VehicleFromPosXY(x, y, data, proc, false);
00323 }
00324 
00336 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00337 {
00338   return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00339 }
00340 
00351 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00352 {
00353   int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00354   int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00355 
00356   Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00357   for (; v != NULL; v = v->next_new_hash) {
00358     if (v->tile != tile) continue;
00359 
00360     Vehicle *a = proc(v, data);
00361     if (find_first && a != NULL) return a;
00362   }
00363 
00364   return NULL;
00365 }
00366 
00380 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00381 {
00382   VehicleFromPos(tile, data, proc, false);
00383 }
00384 
00395 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00396 {
00397   return VehicleFromPos(tile, data, proc, true) != NULL;
00398 }
00399 
00406 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00407 {
00408   byte z = *(byte*)data;
00409 
00410   if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00411   if (v->z_pos > z) return NULL;
00412 
00413   return v;
00414 }
00415 
00421 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00422 {
00423   byte z = GetTileMaxZ(tile);
00424 
00425   /* Value v is not safe in MP games, however, it is used to generate a local
00426    * error message only (which may be different for different machines).
00427    * Such a message does not affect MP synchronisation.
00428    */
00429   Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00430   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00431   return CommandCost();
00432 }
00433 
00435 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00436 {
00437   if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00438   if (v == (const Vehicle *)data) return NULL;
00439 
00440   return v;
00441 }
00442 
00450 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00451 {
00452   /* Value v is not safe in MP games, however, it is used to generate a local
00453    * error message only (which may be different for different machines).
00454    * Such a message does not affect MP synchronisation.
00455    */
00456   Vehicle *v = VehicleFromPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00457   if (v == NULL) v = VehicleFromPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00458 
00459   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00460   return CommandCost();
00461 }
00462 
00463 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00464 {
00465   TrackBits rail_bits = *(TrackBits *)data;
00466 
00467   if (v->type != VEH_TRAIN) return NULL;
00468 
00469   Train *t = Train::From(v);
00470   if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00471 
00472   return v;
00473 }
00474 
00483 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
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, &track_bits, &EnsureNoTrainOnTrackProc, true);
00490   if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00491   return CommandCost();
00492 }
00493 
00494 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00495 {
00496   Vehicle **old_hash = v->old_new_hash;
00497   Vehicle **new_hash;
00498 
00499   if (remove) {
00500     new_hash = NULL;
00501   } else {
00502     int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00503     int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00504     new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00505   }
00506 
00507   if (old_hash == new_hash) return;
00508 
00509   /* Remove from the old position in the hash table */
00510   if (old_hash != NULL) {
00511     if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
00512     *v->prev_new_hash = v->next_new_hash;
00513   }
00514 
00515   /* Insert vehicle at beginning of the new position in the hash table */
00516   if (new_hash != NULL) {
00517     v->next_new_hash = *new_hash;
00518     if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
00519     v->prev_new_hash = new_hash;
00520     *new_hash = v;
00521   }
00522 
00523   /* Remember current hash position */
00524   v->old_new_hash = new_hash;
00525 }
00526 
00527 static Vehicle *_vehicle_position_hash[0x1000];
00528 
00529 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00530 {
00531   UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00532 
00533   Vehicle **old_hash, **new_hash;
00534   int old_x = v->coord.left;
00535   int old_y = v->coord.top;
00536 
00537   new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00538   old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00539 
00540   if (old_hash == new_hash) return;
00541 
00542   /* remove from hash table? */
00543   if (old_hash != NULL) {
00544     if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
00545     *v->prev_hash = v->next_hash;
00546   }
00547 
00548   /* insert into hash table? */
00549   if (new_hash != NULL) {
00550     v->next_hash = *new_hash;
00551     if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
00552     v->prev_hash = new_hash;
00553     *new_hash = v;
00554   }
00555 }
00556 
00557 void ResetVehiclePosHash()
00558 {
00559   Vehicle *v;
00560   FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00561   memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00562   memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00563 }
00564 
00565 void ResetVehicleColourMap()
00566 {
00567   Vehicle *v;
00568   FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00569 }
00570 
00575 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00576 static AutoreplaceMap _vehicles_to_autoreplace;
00577 
00578 void InitializeVehicles()
00579 {
00580   _vehicle_pool.CleanPool();
00581   _cargo_payment_pool.CleanPool();
00582 
00583   _age_cargo_skip_counter = 1;
00584 
00585   _vehicles_to_autoreplace.Reset();
00586   ResetVehiclePosHash();
00587 }
00588 
00589 uint CountVehiclesInChain(const Vehicle *v)
00590 {
00591   uint count = 0;
00592   do count++; while ((v = v->Next()) != NULL);
00593   return count;
00594 }
00595 
00601 void CountCompanyVehicles(CompanyID cid, uint counts[4])
00602 {
00603   for (uint i = 0; i < 4; i++) counts[i] = 0;
00604 
00605   const Vehicle *v;
00606   FOR_ALL_VEHICLES(v) {
00607     if (v->owner == cid && v->IsPrimaryVehicle()) counts[v->type]++;
00608   }
00609 }
00610 
00615 bool Vehicle::IsEngineCountable() const
00616 {
00617   switch (this->type) {
00618     case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
00619     case VEH_TRAIN:
00620       return !Train::From(this)->IsArticulatedPart() && // tenders and other articulated parts
00621           !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
00622     case VEH_ROAD: return RoadVehicle::From(this)->IsRoadVehFront();
00623     case VEH_SHIP: return true;
00624     default: return false; // Only count company buildable vehicles
00625   }
00626 }
00627 
00635 void Vehicle::HandlePathfindingResult(bool path_found)
00636 {
00637   if (path_found) {
00638     /* Route found, is the vehicle marked with "lost" flag? */
00639     if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00640 
00641     /* Clear the flag as the PF's problem was solved. */
00642     ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00643     /* Delete the news item. */
00644     DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00645     return;
00646   }
00647 
00648   /* Were we already lost? */
00649   if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00650 
00651   /* It is first time the problem occurred, set the "lost" flag. */
00652   SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00653   /* Notify user about the event. */
00654   AI::NewEvent(this->owner, new AIEventVehicleLost(this->index));
00655   if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00656     SetDParam(0, this->index);
00657     AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00658   }
00659 }
00660 
00661 void Vehicle::PreDestructor()
00662 {
00663   if (CleaningPool()) return;
00664 
00665   if (Station::IsValidID(this->last_station_visited)) {
00666     Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00667 
00668     HideFillingPercent(&this->fill_percent_te_id);
00669 
00670     delete this->cargo_payment;
00671   }
00672 
00673   if (this->IsEngineCountable()) {
00674     Company::Get(this->owner)->num_engines[this->engine_type]--;
00675     if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00676 
00677     DeleteGroupHighlightOfVehicle(this);
00678     if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
00679     if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00680   }
00681 
00682   if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00683     Aircraft *a = Aircraft::From(this);
00684     Station *st = GetTargetAirportIfValid(a);
00685     if (st != NULL) {
00686       const AirportFTA *layout = st->airport.GetFTA()->layout;
00687       CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00688     }
00689   }
00690 
00691 
00692   if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00693     RoadVehicle *v = RoadVehicle::From(this);
00694     if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00695       /* Leave the drive through roadstop, when you have not already left it. */
00696       RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00697     }
00698   }
00699 
00700   if (this->Previous() == NULL) {
00701     InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00702   }
00703 
00704   if (this->IsPrimaryVehicle()) {
00705     DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00706     DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00707     DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00708     DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00709     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00710     SetWindowDirty(WC_COMPANY, this->owner);
00711     OrderBackup::ClearVehicle(this);
00712   }
00713   InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00714 
00715   this->cargo.Truncate(0);
00716   DeleteVehicleOrders(this);
00717   DeleteDepotHighlightOfVehicle(this);
00718 
00719   extern void StopGlobalFollowVehicle(const Vehicle *v);
00720   StopGlobalFollowVehicle(this);
00721 
00722   ReleaseDisastersTargetingVehicle(this->index);
00723 }
00724 
00725 Vehicle::~Vehicle()
00726 {
00727   free(this->name);
00728 
00729   if (CleaningPool()) return;
00730 
00731   /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
00732    * it may happen that vehicle chain is deleted when visible */
00733   if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00734 
00735   Vehicle *v = this->Next();
00736   this->SetNext(NULL);
00737 
00738   delete v;
00739 
00740   UpdateVehiclePosHash(this, INVALID_COORD, 0);
00741   DeleteVehicleNews(this->index, INVALID_STRING_ID);
00742   DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00743 }
00744 
00749 void VehicleEnteredDepotThisTick(Vehicle *v)
00750 {
00751   /* Vehicle should stop in the depot if it was in 'stopping' state */
00752   _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00753 
00754   /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
00755    * stopping in the depot, so we stop it to ensure that it will not reserve
00756    * the path out of the depot before we might autoreplace it to a different
00757    * engine. The new engine would not own the reserved path we store that we
00758    * stopped the vehicle, so autoreplace can start it again */
00759   v->vehstatus |= VS_STOPPED;
00760 }
00761 
00767 static void RunVehicleDayProc()
00768 {
00769   if (_game_mode != GM_NORMAL) return;
00770 
00771   /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
00772   for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00773     Vehicle *v = Vehicle::Get(i);
00774     if (v == NULL) continue;
00775 
00776     /* Call the 32-day callback if needed */
00777     if ((v->day_counter & 0x1F) == 0) {
00778       uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00779       if (callback != CALLBACK_FAILED) {
00780         if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
00781         if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00782       }
00783     }
00784 
00785     /* This is called once per day for each vehicle, but not in the first tick of the day */
00786     v->OnNewDay();
00787   }
00788 }
00789 
00790 void CallVehicleTicks()
00791 {
00792   _vehicles_to_autoreplace.Clear();
00793 
00794   _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
00795 
00796   RunVehicleDayProc();
00797 
00798   Station *st;
00799   FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00800 
00801   Vehicle *v;
00802   FOR_ALL_VEHICLES(v) {
00803     /* Vehicle could be deleted in this tick */
00804     if (!v->Tick()) {
00805       assert(Vehicle::Get(vehicle_index) == NULL);
00806       continue;
00807     }
00808 
00809     assert(Vehicle::Get(vehicle_index) == v);
00810 
00811     switch (v->type) {
00812       default: break;
00813 
00814       case VEH_TRAIN:
00815       case VEH_ROAD:
00816       case VEH_AIRCRAFT:
00817       case VEH_SHIP:
00818         if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00819 
00820         if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00821         if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00822         if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsRoadVehFront()) continue;
00823 
00824         v->motion_counter += v->cur_speed;
00825         /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
00826         if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00827 
00828         /* Play an alterate running sound every 16 ticks */
00829         if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00830     }
00831   }
00832 
00833   Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00834   for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00835     v = it->first;
00836     /* Autoreplace needs the current company set as the vehicle owner */
00837     cur_company.Change(v->owner);
00838 
00839     /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
00840      * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
00841      * they are already leaving the depot again before being replaced. */
00842     if (it->second) v->vehstatus &= ~VS_STOPPED;
00843 
00844     /* Store the position of the effect as the vehicle pointer will become invalid later */
00845     int x = v->x_pos;
00846     int y = v->y_pos;
00847     int z = v->z_pos;
00848 
00849     const Company *c = Company::Get(_current_company);
00850     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00851     CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00852     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00853 
00854     if (!IsLocalCompany()) continue;
00855 
00856     if (res.Succeeded()) {
00857       ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00858       continue;
00859     }
00860 
00861     StringID error_message = res.GetErrorMessage();
00862     if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00863 
00864     if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00865 
00866     StringID message;
00867     if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00868       message = error_message;
00869     } else {
00870       message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00871     }
00872 
00873     SetDParam(0, v->index);
00874     SetDParam(1, error_message);
00875     AddVehicleNewsItem(message, NS_ADVICE, v->index);
00876   }
00877 
00878   cur_company.Restore();
00879 }
00880 
00885 static void DoDrawVehicle(const Vehicle *v)
00886 {
00887   SpriteID image = v->cur_image;
00888   PaletteID pal = PAL_NONE;
00889 
00890   if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00891 
00892   AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00893     v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00894 }
00895 
00896 void ViewportAddVehicles(DrawPixelInfo *dpi)
00897 {
00898   /* The bounding rectangle */
00899   const int l = dpi->left;
00900   const int r = dpi->left + dpi->width;
00901   const int t = dpi->top;
00902   const int b = dpi->top + dpi->height;
00903 
00904   /* The hash area to scan */
00905   int xl, xu, yl, yu;
00906 
00907   if (dpi->width + 70 < (1 << (7 + 6))) {
00908     xl = GB(l - 70, 7, 6);
00909     xu = GB(r,      7, 6);
00910   } else {
00911     /* scan whole hash row */
00912     xl = 0;
00913     xu = 0x3F;
00914   }
00915 
00916   if (dpi->height + 70 < (1 << (6 + 6))) {
00917     yl = GB(t - 70, 6, 6) << 6;
00918     yu = GB(b,      6, 6) << 6;
00919   } else {
00920     /* scan whole column */
00921     yl = 0;
00922     yu = 0x3F << 6;
00923   }
00924 
00925   for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00926     for (int x = xl;; x = (x + 1) & 0x3F) {
00927       const Vehicle *v = _vehicle_position_hash[x + y]; // already masked & 0xFFF
00928 
00929       while (v != NULL) {
00930         if (!(v->vehstatus & VS_HIDDEN) &&
00931             l <= v->coord.right &&
00932             t <= v->coord.bottom &&
00933             r >= v->coord.left &&
00934             b >= v->coord.top) {
00935           DoDrawVehicle(v);
00936         }
00937         v = v->next_hash;
00938       }
00939 
00940       if (x == xu) break;
00941     }
00942 
00943     if (y == yu) break;
00944   }
00945 }
00946 
00947 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00948 {
00949   Vehicle *found = NULL, *v;
00950   uint dist, best_dist = UINT_MAX;
00951 
00952   if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00953 
00954   x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00955   y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00956 
00957   FOR_ALL_VEHICLES(v) {
00958     if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00959         x >= v->coord.left && x <= v->coord.right &&
00960         y >= v->coord.top && y <= v->coord.bottom) {
00961 
00962       dist = max(
00963         abs(((v->coord.left + v->coord.right) >> 1) - x),
00964         abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00965       );
00966 
00967       if (dist < best_dist) {
00968         found = v;
00969         best_dist = dist;
00970       }
00971     }
00972   }
00973 
00974   return found;
00975 }
00976 
00977 void DecreaseVehicleValue(Vehicle *v)
00978 {
00979   v->value -= v->value >> 8;
00980   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00981 }
00982 
00983 static const byte _breakdown_chance[64] = {
00984     3,   3,   3,   3,   3,   3,   3,   3,
00985     4,   4,   5,   5,   6,   6,   7,   7,
00986     8,   8,   9,   9,  10,  10,  11,  11,
00987    12,  13,  13,  13,  13,  14,  15,  16,
00988    17,  19,  21,  25,  28,  31,  34,  37,
00989    40,  44,  48,  52,  56,  60,  64,  68,
00990    72,  80,  90, 100, 110, 120, 130, 140,
00991   150, 170, 190, 210, 230, 250, 250, 250,
00992 };
00993 
00994 void CheckVehicleBreakdown(Vehicle *v)
00995 {
00996   int rel, rel_old;
00997 
00998   /* decrease reliability */
00999   v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01000   if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01001 
01002   if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01003       _settings_game.difficulty.vehicle_breakdowns < 1 ||
01004       v->cur_speed < 5 || _game_mode == GM_MENU) {
01005     return;
01006   }
01007 
01008   uint32 r = Random();
01009 
01010   /* increase chance of failure */
01011   int chance = v->breakdown_chance + 1;
01012   if (Chance16I(1, 25, r)) chance += 25;
01013   v->breakdown_chance = min(255, chance);
01014 
01015   /* calculate reliability value to use in comparison */
01016   rel = v->reliability;
01017   if (v->type == VEH_SHIP) rel += 0x6666;
01018 
01019   /* reduced breakdowns? */
01020   if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01021 
01022   /* check if to break down */
01023   if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01024     v->breakdown_ctr    = GB(r, 16, 6) + 0x3F;
01025     v->breakdown_delay  = GB(r, 24, 7) + 0x80;
01026     v->breakdown_chance = 0;
01027   }
01028 }
01029 
01030 bool Vehicle::HandleBreakdown()
01031 {
01032   /* Possible states for Vehicle::breakdown_ctr
01033    * 0  - vehicle is running normally
01034    * 1  - vehicle is currently broken down
01035    * 2  - vehicle is going to break down now
01036    * >2 - vehicle is counting down to the actual breakdown event */
01037   switch (this->breakdown_ctr) {
01038     case 0:
01039       return false;
01040 
01041     case 2:
01042       this->breakdown_ctr = 1;
01043 
01044       if (this->breakdowns_since_last_service != 255) {
01045         this->breakdowns_since_last_service++;
01046       }
01047 
01048       this->MarkDirty();
01049       SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01050       SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01051 
01052       if (this->type == VEH_AIRCRAFT) {
01053         /* Aircraft just need this flag, the rest is handled elsewhere */
01054         this->vehstatus |= VS_AIRCRAFT_BROKEN;
01055       } else {
01056         this->cur_speed = 0;
01057 
01058         if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01059           SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01060             (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01061             (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01062         }
01063 
01064         if (!(this->vehstatus & VS_HIDDEN)) {
01065           EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01066           if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01067         }
01068       }
01069       /* FALL THROUGH */
01070     case 1:
01071       /* Aircraft breakdowns end only when arriving at the airport */
01072       if (this->type == VEH_AIRCRAFT) return false;
01073 
01074       /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
01075       if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01076         if (--this->breakdown_delay == 0) {
01077           this->breakdown_ctr = 0;
01078           this->MarkDirty();
01079           SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01080         }
01081       }
01082       return true;
01083 
01084     default:
01085       if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01086       return false;
01087   }
01088 }
01089 
01094 void AgeVehicle(Vehicle *v)
01095 {
01096   if (v->age < MAX_DAY) v->age++;
01097 
01098   int age = v->age - v->max_age;
01099   if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01100       age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01101     v->reliability_spd_dec <<= 1;
01102   }
01103 
01104   SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01105 
01106   /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
01107   if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01108 
01109   /* Don't warn if a renew is active */
01110   if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
01111 
01112   StringID str;
01113   if (age == -DAYS_IN_LEAP_YEAR) {
01114     str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01115   } else if (age == 0) {
01116     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01117   } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01118     str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01119   } else {
01120     return;
01121   }
01122 
01123   SetDParam(0, v->index);
01124   AddVehicleNewsItem(str, NS_ADVICE, v->index);
01125 }
01126 
01133 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01134 {
01135   int count = 0;
01136   int max = 0;
01137   int cars = 0;
01138   int unloading = 0;
01139   bool loading = false;
01140 
01141   const Vehicle *u = v;
01142   /* The station may be NULL when the (colour) string does not need to be set. */
01143   const Station *st = Station::GetIfValid(v->last_station_visited);
01144   assert(colour == NULL || st != NULL);
01145 
01146   /* Count up max and used */
01147   for (; v != NULL; v = v->Next()) {
01148     count += v->cargo.Count();
01149     max += v->cargo_cap;
01150     if (v->cargo_cap != 0 && colour != NULL) {
01151       unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01152       loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01153       cars++;
01154     }
01155   }
01156 
01157   if (colour != NULL) {
01158     if (unloading == 0 && loading) {
01159       *colour = STR_PERCENT_UP;
01160     } else if (cars == unloading || !loading) {
01161       *colour = STR_PERCENT_DOWN;
01162     } else {
01163       *colour = STR_PERCENT_UP_DOWN;
01164     }
01165   }
01166 
01167   /* Train without capacity */
01168   if (max == 0) return 100;
01169 
01170   /* Return the percentage */
01171   return (count * 100) / max;
01172 }
01173 
01174 void VehicleEnterDepot(Vehicle *v)
01175 {
01176   /* Always work with the front of the vehicle */
01177   assert(v == v->First());
01178 
01179   switch (v->type) {
01180     case VEH_TRAIN: {
01181       Train *t = Train::From(v);
01182       SetWindowClassesDirty(WC_TRAINS_LIST);
01183       /* Clear path reservation */
01184       SetDepotReservation(t->tile, false);
01185       if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01186 
01187       UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01188       t->wait_counter = 0;
01189       t->force_proceed = TFP_NONE;
01190       ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01191       t->ConsistChanged(true);
01192       break;
01193     }
01194 
01195     case VEH_ROAD:
01196       SetWindowClassesDirty(WC_ROADVEH_LIST);
01197       break;
01198 
01199     case VEH_SHIP: {
01200       SetWindowClassesDirty(WC_SHIPS_LIST);
01201       Ship *ship = Ship::From(v);
01202       ship->state = TRACK_BIT_DEPOT;
01203       ship->UpdateCache();
01204       ship->UpdateViewport(true, true);
01205       SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01206       break;
01207     }
01208 
01209     case VEH_AIRCRAFT:
01210       SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01211       HandleAircraftEnterHangar(Aircraft::From(v));
01212       break;
01213     default: NOT_REACHED();
01214   }
01215   SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01216 
01217   if (v->type != VEH_TRAIN) {
01218     /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
01219      * 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 */
01220     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01221   }
01222   SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01223 
01224   v->vehstatus |= VS_HIDDEN;
01225   v->cur_speed = 0;
01226 
01227   VehicleServiceInDepot(v);
01228 
01229   TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01230 
01231   if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01232     SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01233 
01234     const Order *real_order = v->GetNextManualOrder(v->cur_order_index);
01235     Order t = v->current_order;
01236     v->current_order.MakeDummy();
01237 
01238     /* Test whether we are heading for this depot. If not, do nothing.
01239      * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
01240     if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01241         real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01242         (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01243       /* We are heading for another depot, keep driving. */
01244       return;
01245     }
01246 
01247     if (t.IsRefit()) {
01248       Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01249       CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01250       cur_company.Restore();
01251 
01252       if (cost.Failed()) {
01253         _vehicles_to_autoreplace[v] = false;
01254         if (v->owner == _local_company) {
01255           /* Notify the user that we stopped the vehicle */
01256           SetDParam(0, v->index);
01257           AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01258         }
01259       } else if (cost.GetCost() != 0) {
01260         v->profit_this_year -= cost.GetCost() << 8;
01261         if (v->owner == _local_company) {
01262           ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01263         }
01264       }
01265     }
01266 
01267     if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01268       /* Part of orders */
01269       UpdateVehicleTimetable(v, true);
01270       v->IncrementOrderIndex();
01271     }
01272     if (t.GetDepotActionType() & ODATFB_HALT) {
01273       /* Vehicles are always stopped on entering depots. Do not restart this one. */
01274       _vehicles_to_autoreplace[v] = false;
01275       if (v->owner == _local_company) {
01276         SetDParam(0, v->index);
01277         AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01278       }
01279       AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01280     }
01281   }
01282 }
01283 
01284 
01292 void VehicleMove(Vehicle *v, bool update_viewport)
01293 {
01294   int img = v->cur_image;
01295   Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01296   const Sprite *spr = GetSprite(img, ST_NORMAL);
01297 
01298   pt.x += spr->x_offs;
01299   pt.y += spr->y_offs;
01300 
01301   UpdateVehiclePosHash(v, pt.x, pt.y);
01302 
01303   Rect old_coord = v->coord;
01304   v->coord.left   = pt.x;
01305   v->coord.top    = pt.y;
01306   v->coord.right  = pt.x + spr->width + 2;
01307   v->coord.bottom = pt.y + spr->height + 2;
01308 
01309   if (update_viewport) {
01310     MarkAllViewportsDirty(
01311       min(old_coord.left,   v->coord.left),
01312       min(old_coord.top,    v->coord.top),
01313       max(old_coord.right,  v->coord.right) + 1,
01314       max(old_coord.bottom, v->coord.bottom) + 1
01315     );
01316   }
01317 }
01318 
01327 void MarkSingleVehicleDirty(const Vehicle *v)
01328 {
01329   MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01330 }
01331 
01337 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01338 {
01339   static const int8 _delta_coord[16] = {
01340     -1,-1,-1, 0, 1, 1, 1, 0, /* x */
01341     -1, 0, 1, 1, 1, 0,-1,-1, /* y */
01342   };
01343 
01344   int x = v->x_pos + _delta_coord[v->direction];
01345   int y = v->y_pos + _delta_coord[v->direction + 8];
01346 
01347   GetNewVehiclePosResult gp;
01348   gp.x = x;
01349   gp.y = y;
01350   gp.old_tile = v->tile;
01351   gp.new_tile = TileVirtXY(x, y);
01352   return gp;
01353 }
01354 
01355 static const Direction _new_direction_table[] = {
01356   DIR_N,  DIR_NW, DIR_W,
01357   DIR_NE, DIR_SE, DIR_SW,
01358   DIR_E,  DIR_SE, DIR_S
01359 };
01360 
01361 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01362 {
01363   int i = 0;
01364 
01365   if (y >= v->y_pos) {
01366     if (y != v->y_pos) i += 3;
01367     i += 3;
01368   }
01369 
01370   if (x >= v->x_pos) {
01371     if (x != v->x_pos) i++;
01372     i++;
01373   }
01374 
01375   Direction dir = v->direction;
01376 
01377   DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01378   if (dirdiff == DIRDIFF_SAME) return dir;
01379   return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01380 }
01381 
01391 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01392 {
01393   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01394 }
01395 
01396 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01397 {
01398   /* Find maximum */
01399   const Vehicle *v;
01400   FOR_ALL_VEHICLES(v) {
01401     if (v->type == type && v->owner == owner) {
01402       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01403     }
01404   }
01405 
01406   if (this->maxid == 0) return;
01407 
01408   /* Reserving 'maxid + 2' because we need:
01409    * - space for the last item (with v->unitnumber == maxid)
01410    * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
01411   this->cache = CallocT<bool>(this->maxid + 2);
01412 
01413   /* Fill the cache */
01414   FOR_ALL_VEHICLES(v) {
01415     if (v->type == type && v->owner == owner) {
01416       this->cache[v->unitnumber] = true;
01417     }
01418   }
01419 }
01420 
01421 UnitID FreeUnitIDGenerator::NextID()
01422 {
01423   if (this->maxid <= this->curid) return ++this->curid;
01424 
01425   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
01426 
01427   return this->curid;
01428 }
01429 
01435 UnitID GetFreeUnitNumber(VehicleType type)
01436 {
01437   /* Check whether it is allowed to build another vehicle. */
01438   uint max_veh;
01439   switch (type) {
01440     case VEH_TRAIN:    max_veh = _settings_game.vehicle.max_trains;   break;
01441     case VEH_ROAD:     max_veh = _settings_game.vehicle.max_roadveh;  break;
01442     case VEH_SHIP:     max_veh = _settings_game.vehicle.max_ships;    break;
01443     case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01444     default: NOT_REACHED();
01445   }
01446 
01447   uint amounts[4];
01448   CountCompanyVehicles(_current_company, amounts);
01449   assert((uint)type < lengthof(amounts));
01450   if (amounts[type] >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
01451 
01452   FreeUnitIDGenerator gen(type, _current_company);
01453 
01454   return gen.NextID();
01455 }
01456 
01457 
01466 bool CanBuildVehicleInfrastructure(VehicleType type)
01467 {
01468   assert(IsCompanyBuildableVehicleType(type));
01469 
01470   if (!Company::IsValidID(_local_company)) return false;
01471   if (_settings_client.gui.always_build_infrastructure) return true;
01472 
01473   UnitID max;
01474   switch (type) {
01475     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
01476     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
01477     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
01478     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01479     default: NOT_REACHED();
01480   }
01481 
01482   /* We can build vehicle infrastructure when we may build the vehicle type */
01483   if (max > 0) {
01484     /* Can we actually build the vehicle type? */
01485     const Engine *e;
01486     FOR_ALL_ENGINES_OF_TYPE(e, type) {
01487       if (HasBit(e->company_avail, _local_company)) return true;
01488     }
01489     return false;
01490   }
01491 
01492   /* We should be able to build infrastructure when we have the actual vehicle type */
01493   const Vehicle *v;
01494   FOR_ALL_VEHICLES(v) {
01495     if (v->owner == _local_company && v->type == type) return true;
01496   }
01497 
01498   return false;
01499 }
01500 
01501 
01509 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01510 {
01511   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01512   const Engine *e = Engine::Get(engine_type);
01513   switch (e->type) {
01514     default: NOT_REACHED();
01515     case VEH_TRAIN:
01516       if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (Train::From(v)->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01517         /* Wagonoverrides use the colour scheme of the front engine.
01518          * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
01519         engine_type = parent_engine_type;
01520         e = Engine::Get(engine_type);
01521         /* Note: Luckily cargo_type is not needed for engines */
01522       }
01523 
01524       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01525       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01526       if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01527         if (!CargoSpec::Get(cargo_type)->is_freight) {
01528           if (parent_engine_type == INVALID_ENGINE) {
01529             return LS_PASSENGER_WAGON_STEAM;
01530           } else {
01531             switch (RailVehInfo(parent_engine_type)->engclass) {
01532               default: NOT_REACHED();
01533               case EC_STEAM:    return LS_PASSENGER_WAGON_STEAM;
01534               case EC_DIESEL:   return LS_PASSENGER_WAGON_DIESEL;
01535               case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01536               case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01537               case EC_MAGLEV:   return LS_PASSENGER_WAGON_MAGLEV;
01538             }
01539           }
01540         } else {
01541           return LS_FREIGHT_WAGON;
01542         }
01543       } else {
01544         bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01545 
01546         switch (e->u.rail.engclass) {
01547           default: NOT_REACHED();
01548           case EC_STEAM:    return LS_STEAM;
01549           case EC_DIESEL:   return is_mu ? LS_DMU : LS_DIESEL;
01550           case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01551           case EC_MONORAIL: return LS_MONORAIL;
01552           case EC_MAGLEV:   return LS_MAGLEV;
01553         }
01554       }
01555 
01556     case VEH_ROAD:
01557       /* Always use the livery of the front */
01558       if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01559         engine_type = parent_engine_type;
01560         e = Engine::Get(engine_type);
01561         cargo_type = v->First()->cargo_type;
01562       }
01563       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01564       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01565 
01566       /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
01567       if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01568         /* Tram */
01569         return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01570       } else {
01571         /* Bus or truck */
01572         return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01573       }
01574 
01575     case VEH_SHIP:
01576       if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01577       if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01578       return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01579 
01580     case VEH_AIRCRAFT:
01581       switch (e->u.air.subtype) {
01582         case AIR_HELI: return LS_HELICOPTER;
01583         case AIR_CTOL: return LS_SMALL_PLANE;
01584         case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01585         default: NOT_REACHED();
01586       }
01587   }
01588 }
01589 
01599 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01600 {
01601   const Company *c = Company::Get(company);
01602   LiveryScheme scheme = LS_DEFAULT;
01603 
01604   /* The default livery is always available for use, but its in_use flag determines
01605    * whether any _other_ liveries are in use. */
01606   if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01607     /* Determine the livery scheme to use */
01608     scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01609 
01610     /* Switch back to the default scheme if the resolved scheme is not in use */
01611     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01612   }
01613 
01614   return &c->livery[scheme];
01615 }
01616 
01617 
01618 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01619 {
01620   PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01621 
01622   /* Return cached value if any */
01623   if (map != PAL_NONE) return map;
01624 
01625   const Engine *e = Engine::Get(engine_type);
01626 
01627   /* Check if we should use the colour map callback */
01628   if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01629     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01630     /* Failure means "use the default two-colour" */
01631     if (callback != CALLBACK_FAILED) {
01632       assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) conincidences with default value (PAL_NONE)
01633       map = GB(callback, 0, 14);
01634       /* If bit 14 is set, then the company colours are applied to the
01635        * map else it's returned as-is. */
01636       if (!HasBit(callback, 14)) {
01637         /* Update cache */
01638         if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01639         return map;
01640       }
01641     }
01642   }
01643 
01644   bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01645 
01646   if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01647 
01648   /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
01649   if (!Company::IsValidID(company)) return map;
01650 
01651   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01652 
01653   map += livery->colour1;
01654   if (twocc) map += livery->colour2 * 16;
01655 
01656   /* Update cache */
01657   if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01658   return map;
01659 }
01660 
01661 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01662 {
01663   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01664 }
01665 
01666 PaletteID GetVehiclePalette(const Vehicle *v)
01667 {
01668   if (v->IsGroundVehicle()) {
01669     return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01670   }
01671 
01672   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01673 }
01674 
01683 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01684 {
01685   if (mail_capacity != NULL) *mail_capacity = 0;
01686   const Engine *e = Engine::Get(v->engine_type);
01687 
01688   if (!e->CanCarryCargo()) return 0;
01689 
01690   if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01691     *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01692   }
01693   CargoID default_cargo = e->GetDefaultCargoType();
01694 
01695   /* Check the refit capacity callback if we are not in the default configuration.
01696    * Note: This might change to become more consistent/flexible/sane, esp. when default cargo is first refittable. */
01697   if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01698       (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01699     uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01700     if (callback != CALLBACK_FAILED) return callback;
01701   }
01702 
01703   /* Get capacity according to property resp. CB */
01704   uint capacity;
01705   switch (e->type) {
01706     case VEH_TRAIN:    capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY,        e->u.rail.capacity); break;
01707     case VEH_ROAD:     capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY,      e->u.road.capacity); break;
01708     case VEH_SHIP:     capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY,         e->u.ship.capacity); break;
01709     case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01710     default: NOT_REACHED();
01711   }
01712 
01713   /* Apply multipliers depending on cargo- and vehicletype.
01714    * Note: This might change to become more consistent/flexible. */
01715   if (e->type != VEH_SHIP) {
01716     if (e->type == VEH_AIRCRAFT) {
01717       if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01718         capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01719       }
01720       if (v->cargo_type == CT_MAIL) return capacity;
01721     } else {
01722       switch (default_cargo) {
01723         case CT_PASSENGERS: break;
01724         case CT_MAIL:
01725         case CT_GOODS: capacity *= 2; break;
01726         default:       capacity *= 4; break;
01727       }
01728     }
01729     switch (v->cargo_type) {
01730       case CT_PASSENGERS: break;
01731       case CT_MAIL:
01732       case CT_GOODS: capacity /= 2; break;
01733       default:       capacity /= 4; break;
01734     }
01735   }
01736 
01737   return capacity;
01738 }
01739 
01740 
01741 void Vehicle::BeginLoading()
01742 {
01743   assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01744 
01745   if (this->current_order.IsType(OT_GOTO_STATION) &&
01746       this->current_order.GetDestination() == this->last_station_visited) {
01747     current_order.MakeLoading(true);
01748     UpdateVehicleTimetable(this, true);
01749 
01750     const Order *order = this->GetOrder(this->cur_order_index);
01751     while (order != NULL && order->IsType(OT_AUTOMATIC)) {
01752       /* Delete order effectively deletes order, so get the next before deleting it. */
01753       order = order->next;
01754       DeleteOrder(this, this->cur_order_index);
01755     }
01756 
01757     /* Furthermore add the Non Stop flag to mark that this station
01758      * is the actual destination of the vehicle, which is (for example)
01759      * necessary to be known for HandleTrainLoading to determine
01760      * whether the train is lost or not; not marking a train lost
01761      * that arrives at random stations is bad. */
01762     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01763 
01764   } else {
01765     /* We weren't scheduled to stop here. Insert an automatic order
01766      * to show that we are stopping here. */
01767     Order *in_list = this->GetOrder(this->cur_order_index);
01768     if ((this->orders.list == NULL || this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID) &&
01769         ((in_list == NULL && this->cur_order_index == 0) ||
01770         (in_list != NULL && (!in_list->IsType(OT_AUTOMATIC) ||
01771         in_list->GetDestination() != this->last_station_visited)))) {
01772       Order *auto_order = new Order();
01773       auto_order->MakeAutomatic(this->last_station_visited);
01774       InsertOrder(this, auto_order, this->cur_order_index);
01775       if (this->cur_order_index > 0) --this->cur_order_index;
01776     }
01777     current_order.MakeLoading(false);
01778   }
01779 
01780   Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01781 
01782   PrepareUnload(this);
01783 
01784   SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01785   SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01786   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01787   SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01788 
01789   Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01790   this->cur_speed = 0;
01791   this->MarkDirty();
01792 }
01793 
01794 void Vehicle::LeaveStation()
01795 {
01796   assert(current_order.IsType(OT_LOADING));
01797 
01798   delete this->cargo_payment;
01799 
01800   /* Only update the timetable if the vehicle was supposed to stop here. */
01801   if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01802 
01803   current_order.MakeLeaveStation();
01804   Station *st = Station::Get(this->last_station_visited);
01805   st->loading_vehicles.remove(this);
01806 
01807   HideFillingPercent(&this->fill_percent_te_id);
01808 
01809   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01810     /* Trigger station animation (trains only) */
01811     if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01812 
01813     SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
01814   }
01815 }
01816 
01817 
01818 void Vehicle::HandleLoading(bool mode)
01819 {
01820   switch (this->current_order.GetType()) {
01821     case OT_LOADING: {
01822       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01823 
01824       /* Not the first call for this tick, or still loading */
01825       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01826           (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01827 
01828       this->PlayLeaveStationSound();
01829 
01830       this->LeaveStation();
01831 
01832       break;
01833     }
01834 
01835     case OT_DUMMY: break;
01836 
01837     default: return;
01838   }
01839 
01840   this->IncrementOrderIndex();
01841 }
01842 
01843 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01844 {
01845   CommandCost ret = CheckOwnership(this->owner);
01846   if (ret.Failed()) return ret;
01847 
01848   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01849   if (this->IsStoppedInDepot()) return CMD_ERROR;
01850 
01851   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01852     bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01853     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01854       /* We called with a different DEPOT_SERVICE setting.
01855        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
01856        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
01857       if (flags & DC_EXEC) {
01858         this->current_order.SetDepotOrderType(ODTF_MANUAL);
01859         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01860         SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01861       }
01862       return CommandCost();
01863     }
01864 
01865     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
01866     if (flags & DC_EXEC) {
01867       /* If the orders to 'goto depot' are in the orders list (forced servicing),
01868        * then skip to the next order; effectively cancelling this forced service */
01869       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
01870 
01871       this->current_order.MakeDummy();
01872       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01873     }
01874     return CommandCost();
01875   }
01876 
01877   TileIndex location;
01878   DestinationID destination;
01879   bool reverse;
01880   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};
01881   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01882 
01883   if (flags & DC_EXEC) {
01884     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01885 
01886     this->dest_tile = location;
01887     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01888     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01889     SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01890 
01891     /* If there is no depot in front, reverse automatically (trains only) */
01892     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01893 
01894     if (this->type == VEH_AIRCRAFT) {
01895       Aircraft *a = Aircraft::From(this);
01896       if (a->state == FLYING && a->targetairport != destination) {
01897         /* The aircraft is now heading for a different hangar than the next in the orders */
01898         extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01899         AircraftNextAirportPos_and_Order(a);
01900       }
01901     }
01902   }
01903 
01904   return CommandCost();
01905 
01906 }
01907 
01908 void Vehicle::UpdateVisualEffect(bool allow_power_change)
01909 {
01910   bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
01911   const Engine *e = Engine::Get(this->engine_type);
01912 
01913   /* Evaluate properties */
01914   byte visual_effect;
01915   switch (e->type) {
01916     case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
01917     case VEH_ROAD:  visual_effect = e->u.road.visual_effect; break;
01918     case VEH_SHIP:  visual_effect = e->u.ship.visual_effect; break;
01919     default:        visual_effect = 1 << VE_DISABLE_EFFECT;  break;
01920   }
01921 
01922   /* Check powered wagon / visual effect callback */
01923   if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
01924     uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
01925 
01926     if (callback != CALLBACK_FAILED) {
01927       callback = GB(callback, 0, 8);
01928       /* Avoid accidentally setting 'visual_effect' to the default value
01929        * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
01930       if (callback == VE_DEFAULT) {
01931         assert(HasBit(callback, VE_DISABLE_EFFECT));
01932         SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
01933       }
01934       visual_effect = callback;
01935     }
01936   }
01937 
01938   /* Apply default values */
01939   if (visual_effect == VE_DEFAULT ||
01940       (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
01941     /* Only train engines have default effects.
01942      * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
01943     if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
01944       if (visual_effect == VE_DEFAULT) {
01945         visual_effect = 1 << VE_DISABLE_EFFECT;
01946       } else {
01947         SetBit(visual_effect, VE_DISABLE_EFFECT);
01948       }
01949     } else {
01950       if (visual_effect == VE_DEFAULT) {
01951         /* Also set the offset */
01952         visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
01953       }
01954       SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
01955     }
01956   }
01957 
01958   this->vcache.cached_vis_effect = visual_effect;
01959 
01960   if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
01961     ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
01962     ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
01963   }
01964 }
01965 
01966 static const int8 _vehicle_smoke_pos[8] = {
01967   1, 1, 1, 0, -1, -1, -1, 0
01968 };
01969 
01970 void Vehicle::ShowVisualEffect() const
01971 {
01972   assert(this->IsPrimaryVehicle());
01973   bool sound = false;
01974 
01975   /* Do not show any smoke when:
01976    * - vehicle smoke is disabled by the player
01977    * - the vehicle is slowing down or stopped (by the player)
01978    * - the vehicle is moving very slowly
01979    */
01980   if (_settings_game.vehicle.smoke_amount == 0 ||
01981       this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
01982       this->cur_speed < 2) {
01983     return;
01984   }
01985   if (this->type == VEH_TRAIN) {
01986     const Train *t = Train::From(this);
01987     /* For trains, do not show any smoke when:
01988      * - the train is reversing
01989      * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
01990      */
01991     if (HasBit(t->flags, VRF_REVERSING) ||
01992         (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
01993         t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
01994       return;
01995     }
01996   }
01997 
01998   const Vehicle *v = this;
01999 
02000   do {
02001     int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02002     byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02003     bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02004 
02005     /* Show no smoke when:
02006      * - Smoke has been disabled for this vehicle
02007      * - The vehicle is not visible
02008      * - The vehicle is on a depot tile
02009      * - The vehicle is on a tunnel tile
02010      * - The vehicle is a train engine that is currently unpowered */
02011     if (disable_effect ||
02012         v->vehstatus & VS_HIDDEN ||
02013         IsDepotTile(v->tile) ||
02014         IsTunnelTile(v->tile) ||
02015         (v->type == VEH_TRAIN &&
02016         !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02017       continue;
02018     }
02019 
02020     int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02021     int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02022 
02023     if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02024       x = -x;
02025       y = -y;
02026     }
02027 
02028     switch (effect_type) {
02029       case VE_TYPE_STEAM:
02030         /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
02031          * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
02032          * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
02033          * REGULATION:
02034          * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
02035         if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02036           CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02037           sound = true;
02038         }
02039         break;
02040 
02041       case VE_TYPE_DIESEL: {
02042         /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
02043          * when smoke emission stops.
02044          * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
02045          * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
02046          * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
02047          * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
02048          * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
02049          * maximum speed no diesel_smoke is emitted.
02050          * REGULATION:
02051          * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
02052          * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
02053         int power_weight_effect = 0;
02054         if (v->type == VEH_TRAIN) {
02055           power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02056         }
02057         if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02058             Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02059           CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02060           sound = true;
02061         }
02062         break;
02063       }
02064 
02065       case VE_TYPE_ELECTRIC:
02066         /* Electric train's spark - more often occurs when train is departing (more load)
02067          * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
02068          * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
02069          * reaching its max. speed, quarter by quarter of it, chance decreases untill the usuall 2,22% at train's top speed.
02070          * REGULATION:
02071          * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
02072         if (GB(v->tick_counter, 0, 2) == 0 &&
02073             Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02074           CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02075           sound = true;
02076         }
02077         break;
02078 
02079       default:
02080         break;
02081     }
02082   } while ((v = v->Next()) != NULL);
02083 
02084   if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02085 }
02086 
02087 void Vehicle::SetNext(Vehicle *next)
02088 {
02089   assert(this != next);
02090 
02091   if (this->next != NULL) {
02092     /* We had an old next vehicle. Update the first and previous pointers */
02093     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02094       v->first = this->next;
02095     }
02096     this->next->previous = NULL;
02097   }
02098 
02099   this->next = next;
02100 
02101   if (this->next != NULL) {
02102     /* A new next vehicle. Update the first and previous pointers */
02103     if (this->next->previous != NULL) this->next->previous->next = NULL;
02104     this->next->previous = this;
02105     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02106       v->first = this->first;
02107     }
02108   }
02109 }
02110 
02111 void Vehicle::AddToShared(Vehicle *shared_chain)
02112 {
02113   assert(this->previous_shared == NULL && this->next_shared == NULL);
02114 
02115   if (!shared_chain->orders.list) {
02116     assert(shared_chain->previous_shared == NULL);
02117     assert(shared_chain->next_shared == NULL);
02118     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02119   }
02120 
02121   this->next_shared     = shared_chain->next_shared;
02122   this->previous_shared = shared_chain;
02123 
02124   shared_chain->next_shared = this;
02125 
02126   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02127 
02128   shared_chain->orders.list->AddVehicle(this);
02129 }
02130 
02131 void Vehicle::RemoveFromShared()
02132 {
02133   /* Remember if we were first and the old window number before RemoveVehicle()
02134    * as this changes first if needed. */
02135   bool were_first = (this->FirstShared() == this);
02136   VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02137 
02138   this->orders.list->RemoveVehicle(this);
02139 
02140   if (!were_first) {
02141     /* We are not the first shared one, so only relink our previous one. */
02142     this->previous_shared->next_shared = this->NextShared();
02143   }
02144 
02145   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02146 
02147 
02148   if (this->orders.list->GetNumVehicles() == 1) {
02149     /* When there is only one vehicle, remove the shared order list window. */
02150     DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02151     InvalidateVehicleOrder(this->FirstShared(), 0);
02152   } else if (were_first) {
02153     /* If we were the first one, update to the new first one.
02154      * Note: FirstShared() is already the new first */
02155     InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02156   }
02157 
02158   this->next_shared     = NULL;
02159   this->previous_shared = NULL;
02160 }
02161 
02167 Order *Vehicle::GetNextManualOrder(int index) const
02168 {
02169   Order *order = this->GetOrder(index);
02170   while(order != NULL && order->IsType(OT_AUTOMATIC)) {
02171     order = order->next;
02172   }
02173   return order;
02174 }
02175 
02176 void StopAllVehicles()
02177 {
02178   Vehicle *v;
02179   FOR_ALL_VEHICLES(v) {
02180     /* Code ripped from CmdStartStopTrain. Can't call it, because of
02181      * ownership problems, so we'll duplicate some code, for now */
02182     v->vehstatus |= VS_STOPPED;
02183     v->MarkDirty();
02184     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
02185     SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
02186   }
02187 }
02188 
02189 void VehiclesYearlyLoop()
02190 {
02191   Vehicle *v;
02192   FOR_ALL_VEHICLES(v) {
02193     if (v->IsPrimaryVehicle()) {
02194       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
02195       Money profit = v->GetDisplayProfitThisYear();
02196       if (v->age >= 730 && profit < 0) {
02197         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02198           SetDParam(0, v->index);
02199           SetDParam(1, profit);
02200           AddVehicleNewsItem(
02201             STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02202             NS_ADVICE,
02203             v->index
02204           );
02205         }
02206         AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02207       }
02208 
02209       v->profit_last_year = v->profit_this_year;
02210       v->profit_this_year = 0;
02211       SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02212     }
02213   }
02214 }
02215 
02216 
02226 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02227 {
02228   const Engine *e = Engine::GetIfValid(engine_type);
02229   assert(e != NULL);
02230 
02231   switch (e->type) {
02232     case VEH_TRAIN:
02233       return (st->facilities & FACIL_TRAIN) != 0;
02234 
02235     case VEH_ROAD:
02236       /* For road vehicles we need the vehicle to know whether it can actually
02237        * use the station, but if it doesn't have facilities for RVs it is
02238        * certainly not possible that the station can be used. */
02239       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02240 
02241     case VEH_SHIP:
02242       return (st->facilities & FACIL_DOCK) != 0;
02243 
02244     case VEH_AIRCRAFT:
02245       return (st->facilities & FACIL_AIRPORT) != 0 &&
02246           (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02247 
02248     default:
02249       return false;
02250   }
02251 }
02252 
02259 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02260 {
02261   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02262 
02263   return CanVehicleUseStation(v->engine_type, st);
02264 }
02265 
02271 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02272 {
02273   assert(this->IsGroundVehicle());
02274   if (this->type == VEH_TRAIN) {
02275     return &Train::From(this)->gcache;
02276   } else {
02277     return &RoadVehicle::From(this)->gcache;
02278   }
02279 }
02280 
02286 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02287 {
02288   assert(this->IsGroundVehicle());
02289   if (this->type == VEH_TRAIN) {
02290     return &Train::From(this)->gcache;
02291   } else {
02292     return &RoadVehicle::From(this)->gcache;
02293   }
02294 }
02295 
02304 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02305 {
02306   if (v->type == VEH_TRAIN) {
02307     Train *u = Train::From(v);
02308     /* If the first vehicle in the selection is part of an articulated vehicle, add the previous parts of the vehicle. */
02309     if (u->IsArticulatedPart()) {
02310       u = u->GetFirstEnginePart();
02311       while (u->index != v->index) {
02312         set.Include(u->index);
02313         u = u->GetNextArticPart();
02314       }
02315     }
02316 
02317     for (;u != NULL && num_vehicles > 0; num_vehicles--, u = u->Next()) {
02318       /* Include current vehicle in the selection. */
02319       set.Include(u->index);
02320 
02321       /* If the vehicle is multiheaded, add the other part too. */
02322       if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02323     }
02324 
02325     /* If the last vehicle is part of an articulated vehicle, add the following parts of the vehicle. */
02326     while (u != NULL && u->IsArticulatedPart()) {
02327       set.Include(u->index);
02328       u = u->Next();
02329     }
02330   }
02331 }

Generated on Fri Dec 31 17:15:41 2010 for OpenTTD by  doxygen 1.6.1