vehicle.cpp

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

Generated on Tue Jul 21 18:48:28 2009 for OpenTTD by  doxygen 1.5.6