vehicle.cpp

Go to the documentation of this file.
00001 /* $Id: vehicle.cpp 16248 2009-05-06 23:21:43Z 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.GetUnloadType() & OUFB_UNLOAD) && 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       if (IsDriveThroughStopTile(v->tile)) return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01223 
01224       /* If vehicle's state is a valid track direction (vehicle is not turning around) return it */
01225       if (!IsReversingRoadTrackdir((Trackdir)v->u.road.state)) return (Trackdir)v->u.road.state;
01226 
01227       /* Vehicle is turning around, get the direction from vehicle's direction */
01228       return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01229 
01230     /* case VEH_AIRCRAFT: case VEH_EFFECT: case VEH_DISASTER: */
01231     default: return INVALID_TRACKDIR;
01232   }
01233 }
01234 
01244 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01245 {
01246   return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01247 }
01248 
01249 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01250 {
01251   /* Find maximum */
01252   const Vehicle *v;
01253   FOR_ALL_VEHICLES(v) {
01254     if (v->type == type && v->owner == owner) {
01255       this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01256     }
01257   }
01258 
01259   if (this->maxid == 0) return;
01260 
01261   this->maxid++; // so there is space for last item (with v->unitnumber == maxid)
01262   this->maxid++; // this one will always be free (well, it will fail when there are 65535 units, so this overflows)
01263 
01264   this->cache = CallocT<bool>(this->maxid);
01265 
01266   /* Fill the cache */
01267   FOR_ALL_VEHICLES(v) {
01268     if (v->type == type && v->owner == owner) {
01269       this->cache[v->unitnumber] = true;
01270     }
01271   }
01272 }
01273 
01274 UnitID FreeUnitIDGenerator::NextID()
01275 {
01276   if (this->maxid <= this->curid) return ++this->curid;
01277 
01278   while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
01279 
01280   return this->curid;
01281 }
01282 
01283 UnitID GetFreeUnitNumber(VehicleType type)
01284 {
01285   FreeUnitIDGenerator gen(type, _current_company);
01286 
01287   return gen.NextID();
01288 }
01289 
01290 
01299 bool CanBuildVehicleInfrastructure(VehicleType type)
01300 {
01301   assert(IsCompanyBuildableVehicleType(type));
01302 
01303   if (!IsValidCompanyID(_local_company)) return false;
01304   if (_settings_client.gui.always_build_infrastructure) return true;
01305 
01306   UnitID max;
01307   switch (type) {
01308     case VEH_TRAIN:    max = _settings_game.vehicle.max_trains; break;
01309     case VEH_ROAD:     max = _settings_game.vehicle.max_roadveh; break;
01310     case VEH_SHIP:     max = _settings_game.vehicle.max_ships; break;
01311     case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01312     default: NOT_REACHED();
01313   }
01314 
01315   /* We can build vehicle infrastructure when we may build the vehicle type */
01316   if (max > 0) {
01317     /* Can we actually build the vehicle type? */
01318     const Engine *e;
01319     FOR_ALL_ENGINES_OF_TYPE(e, type) {
01320       if (HasBit(e->company_avail, _local_company)) return true;
01321     }
01322     return false;
01323   }
01324 
01325   /* We should be able to build infrastructure when we have the actual vehicle type */
01326   const Vehicle *v;
01327   FOR_ALL_VEHICLES(v) {
01328     if (v->owner == _local_company && v->type == type) return true;
01329   }
01330 
01331   return false;
01332 }
01333 
01334 
01335 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01336 {
01337   const Company *c = GetCompany(company);
01338   LiveryScheme scheme = LS_DEFAULT;
01339   CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01340 
01341   /* The default livery is always available for use, but its in_use flag determines
01342    * whether any _other_ liveries are in use. */
01343   if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
01344     /* Determine the livery scheme to use */
01345     const Engine *e = GetEngine(engine_type);
01346     switch (e->type) {
01347       default: NOT_REACHED();
01348       case VEH_TRAIN: {
01349         const RailVehicleInfo *rvi = RailVehInfo(engine_type);
01350         if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (IsArticulatedPart(v) && rvi->railveh_type != RAILVEH_WAGON))) {
01351           /* Wagonoverrides use the coloir scheme of the front engine.
01352            * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
01353           engine_type = parent_engine_type;
01354           e = GetEngine(engine_type);
01355           rvi = RailVehInfo(engine_type);
01356           /* Note: Luckily cargo_type is not needed for engines */
01357         }
01358 
01359         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01360         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01361         if (rvi->railveh_type == RAILVEH_WAGON) {
01362           if (!GetCargo(cargo_type)->is_freight) {
01363             if (parent_engine_type == INVALID_ENGINE) {
01364               scheme = LS_PASSENGER_WAGON_STEAM;
01365             } else {
01366               switch (RailVehInfo(parent_engine_type)->engclass) {
01367                 default: NOT_REACHED();
01368                 case EC_STEAM:    scheme = LS_PASSENGER_WAGON_STEAM;    break;
01369                 case EC_DIESEL:   scheme = LS_PASSENGER_WAGON_DIESEL;   break;
01370                 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
01371                 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
01372                 case EC_MAGLEV:   scheme = LS_PASSENGER_WAGON_MAGLEV;   break;
01373               }
01374             }
01375           } else {
01376             scheme = LS_FREIGHT_WAGON;
01377           }
01378         } else {
01379           bool is_mu = HasBit(EngInfo(engine_type)->misc_flags, EF_RAIL_IS_MU);
01380 
01381           switch (rvi->engclass) {
01382             default: NOT_REACHED();
01383             case EC_STEAM:    scheme = LS_STEAM; break;
01384             case EC_DIESEL:   scheme = is_mu ? LS_DMU : LS_DIESEL;   break;
01385             case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
01386             case EC_MONORAIL: scheme = LS_MONORAIL; break;
01387             case EC_MAGLEV:   scheme = LS_MAGLEV; break;
01388           }
01389         }
01390         break;
01391       }
01392 
01393       case VEH_ROAD: {
01394         /* Always use the livery of the front */
01395         if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01396           engine_type = parent_engine_type;
01397           e = GetEngine(engine_type);
01398           cargo_type = v->First()->cargo_type;
01399         }
01400         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01401         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01402 
01403         /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
01404         if (HasBit(EngInfo(engine_type)->misc_flags, EF_ROAD_TRAM)) {
01405           /* Tram */
01406           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01407         } else {
01408           /* Bus or truck */
01409           scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01410         }
01411         break;
01412       }
01413 
01414       case VEH_SHIP: {
01415         if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01416         if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
01417         scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01418         break;
01419       }
01420 
01421       case VEH_AIRCRAFT: {
01422         switch (e->u.air.subtype) {
01423           case AIR_HELI: scheme = LS_HELICOPTER; break;
01424           case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
01425           case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
01426         }
01427         break;
01428       }
01429     }
01430 
01431     /* Switch back to the default scheme if the resolved scheme is not in use */
01432     if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01433   }
01434 
01435   return &c->livery[scheme];
01436 }
01437 
01438 
01439 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01440 {
01441   SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01442 
01443   /* Return cached value if any */
01444   if (map != PAL_NONE) return map;
01445 
01446   /* Check if we should use the colour map callback */
01447   if (HasBit(EngInfo(engine_type)->callbackmask, CBM_VEHICLE_COLOUR_REMAP)) {
01448     uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01449     /* A return value of 0xC000 is stated to "use the default two-colour
01450      * maps" which happens to be the failure action too... */
01451     if (callback != CALLBACK_FAILED && callback != 0xC000) {
01452       map = GB(callback, 0, 14);
01453       /* If bit 14 is set, then the company colours are applied to the
01454        * map else it's returned as-is. */
01455       if (!HasBit(callback, 14)) {
01456         /* Update cache */
01457         if (v != NULL) ((Vehicle*)v)->colourmap = map;
01458         return map;
01459       }
01460     }
01461   }
01462 
01463   bool twocc = HasBit(EngInfo(engine_type)->misc_flags, EF_USES_2CC);
01464 
01465   if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
01466 
01467   const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
01468 
01469   map += livery->colour1;
01470   if (twocc) map += livery->colour2 * 16;
01471 
01472   /* Update cache */
01473   if (v != NULL) ((Vehicle*)v)->colourmap = map;
01474   return map;
01475 }
01476 
01477 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
01478 {
01479   return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01480 }
01481 
01482 SpriteID GetVehiclePalette(const Vehicle *v)
01483 {
01484   if (v->type == VEH_TRAIN) {
01485     return GetEngineColourMap(v->engine_type, v->owner, v->u.rail.first_engine, v);
01486   } else if (v->type == VEH_ROAD) {
01487     return GetEngineColourMap(v->engine_type, v->owner, v->u.road.first_engine, v);
01488   }
01489 
01490   return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01491 }
01492 
01493 
01494 void Vehicle::BeginLoading()
01495 {
01496   assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01497 
01498   if (this->current_order.IsType(OT_GOTO_STATION) &&
01499       this->current_order.GetDestination() == this->last_station_visited) {
01500     current_order.MakeLoading(true);
01501     UpdateVehicleTimetable(this, true);
01502 
01503     /* Furthermore add the Non Stop flag to mark that this station
01504      * is the actual destination of the vehicle, which is (for example)
01505      * necessary to be known for HandleTrainLoading to determine
01506      * whether the train is lost or not; not marking a train lost
01507      * that arrives at random stations is bad. */
01508     this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01509 
01510   } else {
01511     current_order.MakeLoading(false);
01512   }
01513 
01514   GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
01515 
01516   VehiclePayment(this);
01517 
01518   InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner);
01519   InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01520   InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
01521   InvalidateWindow(WC_STATION_VIEW, this->last_station_visited);
01522 
01523   GetStation(this->last_station_visited)->MarkTilesDirty(true);
01524   this->cur_speed = 0;
01525   this->MarkDirty();
01526 }
01527 
01528 void Vehicle::LeaveStation()
01529 {
01530   assert(current_order.IsType(OT_LOADING));
01531 
01532   /* Only update the timetable if the vehicle was supposed to stop here. */
01533   if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01534 
01535   current_order.MakeLeaveStation();
01536   Station *st = GetStation(this->last_station_visited);
01537   st->loading_vehicles.remove(this);
01538 
01539   HideFillingPercent(&this->fill_percent_te_id);
01540 
01541   if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01542     /* Trigger station animation (trains only) */
01543     if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
01544 
01545     /* Try to reserve a path when leaving the station as we
01546      * might not be marked as wanting a reservation, e.g.
01547      * when an overlength train gets turned around in a station. */
01548     if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(GetVehicleTrackdir(this)), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
01549       TryPathReserve(this, true, true);
01550     }
01551   }
01552 }
01553 
01554 
01555 void Vehicle::HandleLoading(bool mode)
01556 {
01557   switch (this->current_order.GetType()) {
01558     case OT_LOADING: {
01559       uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01560 
01561       /* Not the first call for this tick, or still loading */
01562       if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01563           (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01564 
01565       this->PlayLeaveStationSound();
01566 
01567       bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
01568       this->LeaveStation();
01569 
01570       /* If this was not the final order, don't remove it from the list. */
01571       if (!at_destination_station) return;
01572       break;
01573     }
01574 
01575     case OT_DUMMY: break;
01576 
01577     default: return;
01578   }
01579 
01580   this->cur_order_index++;
01581   InvalidateVehicleOrder(this, 0);
01582 }
01583 
01584 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01585 {
01586   if (!CheckOwnership(this->owner)) return CMD_ERROR;
01587   if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01588   if (this->IsStoppedInDepot()) return CMD_ERROR;
01589 
01590   if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01591     bool halt_in_depot = this->current_order.GetDepotActionType() & ODATFB_HALT;
01592     if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01593       /* We called with a different DEPOT_SERVICE setting.
01594        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
01595        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
01596       if (flags & DC_EXEC) {
01597         this->current_order.SetDepotOrderType(ODTF_MANUAL);
01598         this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01599         InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01600       }
01601       return CommandCost();
01602     }
01603 
01604     if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
01605     if (flags & DC_EXEC) {
01606       /* If the orders to 'goto depot' are in the orders list (forced servicing),
01607        * then skip to the next order; effectively cancelling this forced service */
01608       if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->cur_order_index++;
01609 
01610       this->current_order.MakeDummy();
01611       InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01612     }
01613     return CommandCost();
01614   }
01615 
01616   TileIndex location;
01617   DestinationID destination;
01618   bool reverse;
01619   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};
01620   if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01621 
01622   if (flags & DC_EXEC) {
01623     if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01624 
01625     this->dest_tile = location;
01626     this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01627     if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01628     InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01629 
01630     /* If there is no depot in front, reverse automatically (trains only) */
01631     if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01632 
01633     if (this->type == VEH_AIRCRAFT && this->u.air.state == FLYING && this->u.air.targetairport != destination) {
01634       /* The aircraft is now heading for a different hangar than the next in the orders */
01635       extern void AircraftNextAirportPos_and_Order(Vehicle *v);
01636       AircraftNextAirportPos_and_Order(this);
01637     }
01638   }
01639 
01640   return CommandCost();
01641 
01642 }
01643 
01644 void Vehicle::SetNext(Vehicle *next)
01645 {
01646   if (this->next != NULL) {
01647     /* We had an old next vehicle. Update the first and previous pointers */
01648     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01649       v->first = this->next;
01650     }
01651     this->next->previous = NULL;
01652   }
01653 
01654   this->next = next;
01655 
01656   if (this->next != NULL) {
01657     /* A new next vehicle. Update the first and previous pointers */
01658     if (this->next->previous != NULL) this->next->previous->next = NULL;
01659     this->next->previous = this;
01660     for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01661       v->first = this->first;
01662     }
01663   }
01664 }
01665 
01666 void Vehicle::AddToShared(Vehicle *shared_chain)
01667 {
01668   assert(this->previous_shared == NULL && this->next_shared == NULL);
01669 
01670   if (!shared_chain->orders.list) {
01671     assert(shared_chain->previous_shared == NULL);
01672     assert(shared_chain->next_shared == NULL);
01673     this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
01674   }
01675 
01676   this->next_shared     = shared_chain->next_shared;
01677   this->previous_shared = shared_chain;
01678 
01679   shared_chain->next_shared = this;
01680 
01681   if (this->next_shared != NULL) this->next_shared->previous_shared = this;
01682 
01683   shared_chain->orders.list->AddVehicle(this);
01684 }
01685 
01686 void Vehicle::RemoveFromShared()
01687 {
01688   /* Remember if we were first and the old window number before RemoveVehicle()
01689    * as this changes first if needed. */
01690   bool were_first = (this->FirstShared() == this);
01691   uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
01692 
01693   this->orders.list->RemoveVehicle(this);
01694 
01695   if (!were_first) {
01696     /* We are not the first shared one, so only relink our previous one. */
01697     this->previous_shared->next_shared = this->NextShared();
01698   }
01699 
01700   if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
01701 
01702 
01703   if (this->orders.list->GetNumVehicles() == 1) {
01704     /* When there is only one vehicle, remove the shared order list window. */
01705     DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
01706     InvalidateVehicleOrder(this->FirstShared(), 0);
01707   } else if (were_first) {
01708     /* If we were the first one, update to the new first one.
01709      * Note: FirstShared() is already the new first */
01710     InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
01711   }
01712 
01713   this->next_shared     = NULL;
01714   this->previous_shared = NULL;
01715 }
01716 
01717 void StopAllVehicles()
01718 {
01719   Vehicle *v;
01720   FOR_ALL_VEHICLES(v) {
01721     /* Code ripped from CmdStartStopTrain. Can't call it, because of
01722      * ownership problems, so we'll duplicate some code, for now */
01723     v->vehstatus |= VS_STOPPED;
01724     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01725     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01726   }
01727 }
01728 
01729 void VehiclesYearlyLoop()
01730 {
01731   Vehicle *v;
01732   FOR_ALL_VEHICLES(v) {
01733     if (v->IsPrimaryVehicle()) {
01734       /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
01735       Money profit = v->GetDisplayProfitThisYear();
01736       if (v->age >= 730 && profit < 0) {
01737         if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
01738           SetDParam(0, v->index);
01739           SetDParam(1, profit);
01740           AddNewsItem(
01741             STR_VEHICLE_IS_UNPROFITABLE,
01742             NS_ADVICE,
01743             v->index,
01744             0);
01745         }
01746         AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
01747       }
01748 
01749       v->profit_last_year = v->profit_this_year;
01750       v->profit_this_year = 0;
01751       InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01752     }
01753   }
01754 }
01755 
01756 
01766 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
01767 {
01768   assert(IsEngineIndex(engine_type));
01769   const Engine *e = GetEngine(engine_type);
01770 
01771   switch (e->type) {
01772     case VEH_TRAIN:
01773       return (st->facilities & FACIL_TRAIN) != 0;
01774 
01775     case VEH_ROAD:
01776       /* For road vehicles we need the vehicle to know whether it can actually
01777        * use the station, but if it doesn't have facilities for RVs it is
01778        * certainly not possible that the station can be used. */
01779       return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
01780 
01781     case VEH_SHIP:
01782       return (st->facilities & FACIL_DOCK) != 0;
01783 
01784     case VEH_AIRCRAFT:
01785       return (st->facilities & FACIL_AIRPORT) != 0 &&
01786           (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
01787 
01788     default:
01789       return false;
01790   }
01791 }
01792 
01799 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
01800 {
01801   if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(v) != NULL;
01802 
01803   return CanVehicleUseStation(v->engine_type, st);
01804 }

Generated on Mon May 11 15:48:09 2009 for OpenTTD by  doxygen 1.5.6