vehicle.cpp

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

Generated on Wed Jul 15 20:36:04 2009 for OpenTTD by  doxygen 1.5.6