vehicle.cpp

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

Generated on Sun Mar 15 22:49:51 2009 for openttd by  doxygen 1.5.6