vehicle.cpp

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

Generated on Wed Jun 3 19:05:16 2009 for OpenTTD by  doxygen 1.5.6