vehicle.cpp

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

Generated on Mon Jun 8 23:04:09 2009 for OpenTTD by  doxygen 1.5.6