# ai_vehicle.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "../../stdafx.h"
    00013 #include "ai_engine.hpp"
    00014 #include "ai_cargo.hpp"
    00015 #include "ai_gamesettings.hpp"
    00016 #include "ai_group.hpp"
    00017 #include "../ai_instance.hpp"
    00018 #include "../../company_func.h"
    00019 #include "../../string_func.h"
    00020 #include "../../strings_func.h"
    00021 #include "../../command_func.h"
    00022 #include "../../roadveh.h"
    00023 #include "../../train.h"
    00024 #include "../../vehicle_func.h"
    00025 #include "table/strings.h"
    00026
    00027 /* static */ bool AIVehicle::IsValidVehicle(VehicleID vehicle_id)
    00028 {
    00029   const Vehicle *v = ::Vehicle::GetIfValid(vehicle_id);
    00030   return v != NULL && v->owner == _current_company && (v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && ::Train::From(v)->IsFreeWagon()));
    00031 }
    00032
    00033 /* static */ int32 AIVehicle::GetNumWagons(VehicleID vehicle_id)
    00034 {
    00035   if (!IsValidVehicle(vehicle_id)) return -1;
    00036
    00037   int num = 1;
    00038
    00039   const Train *v = ::Train::GetIfValid(vehicle_id);
    00040   if (v != NULL) {
    00041     while ((v = v->GetNextUnit()) != NULL) num++;
    00042   }
    00043
    00044   return num;
    00045 }
    00046
    00047 /* static */ int AIVehicle::GetLength(VehicleID vehicle_id)
    00048 {
    00049   if (!IsValidVehicle(vehicle_id)) return -1;
    00050
    00051   const Vehicle *v = ::Vehicle::Get(vehicle_id);
    00052   return v->IsGroundVehicle() ? v->GetGroundVehicleCache()->cached_total_length : -1;
    00053 }
    00054
    00055 /* static */ VehicleID AIVehicle::BuildVehicle(TileIndex depot, EngineID engine_id)
    00056 {
    00057   EnforcePrecondition(VEHICLE_INVALID, AIEngine::IsBuildable(engine_id));
    00058
    00059 	::VehicleType type = ::Engine::Get(engine_id)->type;
    00060
    00061   EnforcePreconditionCustomError(VEHICLE_INVALID, !AIGameSettings::IsDisabledVehicleType((AIVehicle::VehicleType)type), AIVehicle::ERR_VEHICLE_BUILD_DISABLED);
    00062
    00063   if (!AIObject::DoCommand(depot, engine_id, 0, ::GetCmdBuildVeh(type), NULL, &AIInstance::DoCommandReturnVehicleID)) return VEHICLE_INVALID;
    00064
    00065   /* In case of test-mode, we return VehicleID 0 */
    00066   return 0;
    00067 }
    00068
    00069 /* static */ VehicleID AIVehicle::CloneVehicle(TileIndex depot, VehicleID vehicle_id, bool share_orders)
    00070 {
    00071   EnforcePrecondition(false, IsValidVehicle(vehicle_id));
    00072
    00073   if (!AIObject::DoCommand(depot, vehicle_id, share_orders, CMD_CLONE_VEHICLE, NULL, &AIInstance::DoCommandReturnVehicleID)) return VEHICLE_INVALID;
    00074
    00075   /* In case of test-mode, we return VehicleID 0 */
    00076   return 0;
    00077 }
    00078
    00079 /* static */ bool AIVehicle::_MoveWagonInternal(VehicleID source_vehicle_id, int source_wagon, bool move_attached_wagons, int dest_vehicle_id, int dest_wagon)
    00080 {
    00081   EnforcePrecondition(false, IsValidVehicle(source_vehicle_id) && source_wagon < GetNumWagons(source_vehicle_id));
    00082   EnforcePrecondition(false, dest_vehicle_id == -1 || (IsValidVehicle(dest_vehicle_id) && dest_wagon < GetNumWagons(dest_vehicle_id)));
    00083   EnforcePrecondition(false, ::Vehicle::Get(source_vehicle_id)->type == VEH_TRAIN);
    00084   EnforcePrecondition(false, dest_vehicle_id == -1 || ::Vehicle::Get(dest_vehicle_id)->type == VEH_TRAIN);
    00085
    00086   const Train *v = ::Train::Get(source_vehicle_id);
    00087   while (source_wagon-- > 0) v = v->GetNextUnit();
    00088   const Train *w = NULL;
    00089   if (dest_vehicle_id != -1) {
    00090     w = ::Train::Get(dest_vehicle_id);
    00091     while (dest_wagon-- > 0) w = w->GetNextUnit();
    00092   }
    00093
    00094   return AIObject::DoCommand(0, v->index | (move_attached_wagons ? 1 : 0) << 20, w == NULL ? ::INVALID_VEHICLE : w->index, CMD_MOVE_RAIL_VEHICLE);
    00095 }
    00096
    00097 /* static */ bool AIVehicle::MoveWagon(VehicleID source_vehicle_id, int source_wagon, int dest_vehicle_id, int dest_wagon)
    00098 {
    00099   return _MoveWagonInternal(source_vehicle_id, source_wagon, false, dest_vehicle_id, dest_wagon);
    00100 }
    00101
    00102 /* static */ bool AIVehicle::MoveWagonChain(VehicleID source_vehicle_id, int source_wagon, int dest_vehicle_id, int dest_wagon)
    00103 {
    00104   return _MoveWagonInternal(source_vehicle_id, source_wagon, true, dest_vehicle_id, dest_wagon);
    00105 }
    00106
    00107 /* static */ int AIVehicle::GetRefitCapacity(VehicleID vehicle_id, CargoID cargo)
    00108 {
    00109   if (!IsValidVehicle(vehicle_id)) return -1;
    00110   if (!AICargo::IsValidCargo(cargo)) return -1;
    00111
    00112   CommandCost res = ::DoCommand(0, vehicle_id, cargo, DC_QUERY_COST, GetCmdRefitVeh(::Vehicle::Get(vehicle_id)));
    00113   return res.Succeeded() ? _returned_refit_capacity : -1;
    00114 }
    00115
    00116 /* static */ bool AIVehicle::RefitVehicle(VehicleID vehicle_id, CargoID cargo)
    00117 {
    00118   EnforcePrecondition(false, IsValidVehicle(vehicle_id) && AICargo::IsValidCargo(cargo));
    00119
    00120   return AIObject::DoCommand(0, vehicle_id, cargo, GetCmdRefitVeh(::Vehicle::Get(vehicle_id)));
    00121 }
    00122
    00123
    00124 /* static */ bool AIVehicle::SellVehicle(VehicleID vehicle_id)
    00125 {
    00126   EnforcePrecondition(false, IsValidVehicle(vehicle_id));
    00127
    00128   const Vehicle *v = ::Vehicle::Get(vehicle_id);
    00129   return AIObject::DoCommand(0, vehicle_id | (v->type == VEH_TRAIN ? 1 : 0) << 20, 0, GetCmdSellVeh(v));
    00130 }
    00131
    00132 /* static */ bool AIVehicle::_SellWagonInternal(VehicleID vehicle_id, int wagon, bool sell_attached_wagons)
    00133 {
    00134   EnforcePrecondition(false, IsValidVehicle(vehicle_id) && wagon < GetNumWagons(vehicle_id));
    00135   EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
    00136
    00137   const Train *v = ::Train::Get(vehicle_id);
    00138   while (wagon-- > 0) v = v->GetNextUnit();
    00139
    00140   return AIObject::DoCommand(0, v->index | (sell_attached_wagons ? 1 : 0) << 20, 0, CMD_SELL_VEHICLE);
    00141 }
    00142
    00143 /* static */ bool AIVehicle::SellWagon(VehicleID vehicle_id, int wagon)
    00144 {
    00145   return _SellWagonInternal(vehicle_id, wagon, false);
    00146 }
    00147
    00148 /* static */ bool AIVehicle::SellWagonChain(VehicleID vehicle_id, int wagon)
    00149 {
    00150   return _SellWagonInternal(vehicle_id, wagon, true);
    00151 }
    00152
    00153 /* static */ bool AIVehicle::SendVehicleToDepot(VehicleID vehicle_id)
    00154 {
    00155   EnforcePrecondition(false, IsValidVehicle(vehicle_id));
    00156
    00157   return AIObject::DoCommand(0, vehicle_id, 0, GetCmdSendToDepot(::Vehicle::Get(vehicle_id)));
    00158 }
    00159
    00160 /* static */ bool AIVehicle::SendVehicleToDepotForServicing(VehicleID vehicle_id)
    00161 {
    00162   EnforcePrecondition(false, IsValidVehicle(vehicle_id));
    00163
    00164   return AIObject::DoCommand(0, vehicle_id | DEPOT_SERVICE, 0, GetCmdSendToDepot(::Vehicle::Get(vehicle_id)));
    00165 }
    00166
    00167 /* static */ bool AIVehicle::IsInDepot(VehicleID vehicle_id)
    00168 {
    00169   if (!IsValidVehicle(vehicle_id)) return false;
    00170   return ::Vehicle::Get(vehicle_id)->IsInDepot();
    00171 }
    00172
    00173 /* static */ bool AIVehicle::IsStoppedInDepot(VehicleID vehicle_id)
    00174 {
    00175   if (!IsValidVehicle(vehicle_id)) return false;
    00176   return ::Vehicle::Get(vehicle_id)->IsStoppedInDepot();
    00177 }
    00178
    00179 /* static */ bool AIVehicle::StartStopVehicle(VehicleID vehicle_id)
    00180 {
    00181   EnforcePrecondition(false, IsValidVehicle(vehicle_id));
    00182
    00183   return AIObject::DoCommand(0, vehicle_id, 0, CMD_START_STOP_VEHICLE);
    00184 }
    00185
    00186 /* static */ bool AIVehicle::ReverseVehicle(VehicleID vehicle_id)
    00187 {
    00188   EnforcePrecondition(false, IsValidVehicle(vehicle_id));
    00189   EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_ROAD || ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
    00190
    00191   switch (::Vehicle::Get(vehicle_id)->type) {
    00192     case VEH_ROAD: return AIObject::DoCommand(0, vehicle_id, 0, CMD_TURN_ROADVEH);
    00193     case VEH_TRAIN: return AIObject::DoCommand(0, vehicle_id, 0, CMD_REVERSE_TRAIN_DIRECTION);
    00194     default: NOT_REACHED();
    00195   }
    00196 }
    00197
    00198 /* static */ bool AIVehicle::SetName(VehicleID vehicle_id, const char *name)
    00199 {
    00200   EnforcePrecondition(false, IsValidVehicle(vehicle_id));
    00201   EnforcePrecondition(false, !::StrEmpty(name));
    00202   EnforcePreconditionCustomError(false, ::Utf8StringLength(name) < MAX_LENGTH_VEHICLE_NAME_CHARS, AIError::ERR_PRECONDITION_STRING_TOO_LONG);
    00203
    00204   return AIObject::DoCommand(0, vehicle_id, 0, CMD_RENAME_VEHICLE, name);
    00205 }
    00206
    00207 /* static */ TileIndex AIVehicle::GetLocation(VehicleID vehicle_id)
    00208 {
    00209   if (!IsValidVehicle(vehicle_id)) return INVALID_TILE;
    00210
    00211   const Vehicle *v = ::Vehicle::Get(vehicle_id);
    00212   if (v->type == VEH_AIRCRAFT) {
    00213     uint x = Clamp(v->x_pos / TILE_SIZE, 0, ::MapSizeX() - 2);
    00214     uint y = Clamp(v->y_pos / TILE_SIZE, 0, ::MapSizeY() - 2);
    00215     return ::TileXY(x, y);
    00216   }
    00217
    00218   return v->tile;
    00219 }
    00220
    00221 /* static */ EngineID AIVehicle::GetEngineType(VehicleID vehicle_id)
    00222 {
    00223   if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
    00224
    00225   return ::Vehicle::Get(vehicle_id)->engine_type;
    00226 }
    00227
    00228 /* static */ EngineID AIVehicle::GetWagonEngineType(VehicleID vehicle_id, int wagon)
    00229 {
    00230   if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
    00231   if (wagon >= GetNumWagons(vehicle_id)) return INVALID_ENGINE;
    00232
    00233   const Vehicle *v = ::Vehicle::Get(vehicle_id);
    00234   if (v->type == VEH_TRAIN) {
    00235     while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
    00236   }
    00237   return v->engine_type;
    00238 }
    00239
    00240 /* static */ int32 AIVehicle::GetUnitNumber(VehicleID vehicle_id)
    00241 {
    00242   if (!IsValidVehicle(vehicle_id)) return -1;
    00243
    00244   return ::Vehicle::Get(vehicle_id)->unitnumber;
    00245 }
    00246
    00247 /* static */ char *AIVehicle::GetName(VehicleID vehicle_id)
    00248 {
    00249   if (!IsValidVehicle(vehicle_id)) return NULL;
    00250
    00251   static const int len = 64;
    00252   char *vehicle_name = MallocT<char>(len);
    00253
    00254 	::SetDParam(0, vehicle_id);
    00255   ::GetString(vehicle_name, STR_VEHICLE_NAME, &vehicle_name[len - 1]);
    00256   return vehicle_name;
    00257 }
    00258
    00259 /* static */ int32 AIVehicle::GetAge(VehicleID vehicle_id)
    00260 {
    00261   if (!IsValidVehicle(vehicle_id)) return -1;
    00262
    00263   return ::Vehicle::Get(vehicle_id)->age;
    00264 }
    00265
    00266 /* static */ int32 AIVehicle::GetWagonAge(VehicleID vehicle_id, int wagon)
    00267 {
    00268   if (!IsValidVehicle(vehicle_id)) return -1;
    00269   if (wagon >= GetNumWagons(vehicle_id)) return -1;
    00270
    00271   const Vehicle *v = ::Vehicle::Get(vehicle_id);
    00272   if (v->type == VEH_TRAIN) {
    00273     while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
    00274   }
    00275   return v->age;
    00276 }
    00277
    00278 /* static */ int32 AIVehicle::GetMaxAge(VehicleID vehicle_id)
    00279 {
    00280   if (!IsValidVehicle(vehicle_id)) return -1;
    00281
    00282   return ::Vehicle::Get(vehicle_id)->max_age;
    00283 }
    00284
    00285 /* static */ int32 AIVehicle::GetAgeLeft(VehicleID vehicle_id)
    00286 {
    00287   if (!IsValidVehicle(vehicle_id)) return -1;
    00288
    00289   return ::Vehicle::Get(vehicle_id)->max_age - ::Vehicle::Get(vehicle_id)->age;
    00290 }
    00291
    00292 /* static */ int32 AIVehicle::GetCurrentSpeed(VehicleID vehicle_id)
    00293 {
    00294   if (!IsValidVehicle(vehicle_id)) return -1;
    00295
    00296   return ::Vehicle::Get(vehicle_id)->GetDisplaySpeed(); // km-ish/h
    00297 }
    00298
    00299 /* static */ AIVehicle::VehicleState AIVehicle::GetState(VehicleID vehicle_id)
    00300 {
    00301   if (!IsValidVehicle(vehicle_id)) return AIVehicle::VS_INVALID;
    00302
    00303   const Vehicle *v = ::Vehicle::Get(vehicle_id);
    00304   byte vehstatus = v->vehstatus;
    00305
    00306   if (vehstatus & ::VS_CRASHED) return AIVehicle::VS_CRASHED;
    00307   if (v->breakdown_ctr != 0) return AIVehicle::VS_BROKEN;
    00308   if (v->IsStoppedInDepot()) return AIVehicle::VS_IN_DEPOT;
    00309   if (vehstatus & ::VS_STOPPED) return AIVehicle::VS_STOPPED;
    00310   if (v->current_order.IsType(OT_LOADING)) return AIVehicle::VS_AT_STATION;
    00311   return AIVehicle::VS_RUNNING;
    00312 }
    00313
    00314 /* static */ Money AIVehicle::GetRunningCost(VehicleID vehicle_id)
    00315 {
    00316   if (!IsValidVehicle(vehicle_id)) return -1;
    00317
    00318   return ::Vehicle::Get(vehicle_id)->GetRunningCost() >> 8;
    00319 }
    00320
    00321 /* static */ Money AIVehicle::GetProfitThisYear(VehicleID vehicle_id)
    00322 {
    00323   if (!IsValidVehicle(vehicle_id)) return -1;
    00324
    00325   return ::Vehicle::Get(vehicle_id)->GetDisplayProfitThisYear();
    00326 }
    00327
    00328 /* static */ Money AIVehicle::GetProfitLastYear(VehicleID vehicle_id)
    00329 {
    00330   if (!IsValidVehicle(vehicle_id)) return -1;
    00331
    00332   return ::Vehicle::Get(vehicle_id)->GetDisplayProfitLastYear();
    00333 }
    00334
    00335 /* static */ Money AIVehicle::GetCurrentValue(VehicleID vehicle_id)
    00336 {
    00337   if (!IsValidVehicle(vehicle_id)) return -1;
    00338
    00339   return ::Vehicle::Get(vehicle_id)->value;
    00340 }
    00341
    00342 /* static */ AIVehicle::VehicleType AIVehicle::GetVehicleType(VehicleID vehicle_id)
    00343 {
    00344   if (!IsValidVehicle(vehicle_id)) return VT_INVALID;
    00345
    00346   switch (::Vehicle::Get(vehicle_id)->type) {
    00347     case VEH_ROAD:     return VT_ROAD;
    00348     case VEH_TRAIN:    return VT_RAIL;
    00349     case VEH_SHIP:     return VT_WATER;
    00350     case VEH_AIRCRAFT: return VT_AIR;
    00351     default:           return VT_INVALID;
    00352   }
    00353 }
    00354
    00355 /* static */ AIRoad::RoadType AIVehicle::GetRoadType(VehicleID vehicle_id)
    00356 {
    00357   if (!IsValidVehicle(vehicle_id)) return AIRoad::ROADTYPE_INVALID;
    00358   if (GetVehicleType(vehicle_id) != VT_ROAD) return AIRoad::ROADTYPE_INVALID;
    00359
    00360   return (AIRoad::RoadType)(::RoadVehicle::Get(vehicle_id))->roadtype;
    00361 }
    00362
    00363 /* static */ int32 AIVehicle::GetCapacity(VehicleID vehicle_id, CargoID cargo)
    00364 {
    00365   if (!IsValidVehicle(vehicle_id)) return -1;
    00366   if (!AICargo::IsValidCargo(cargo)) return -1;
    00367
    00368   uint32 amount = 0;
    00369   for (const Vehicle *v = ::Vehicle::Get(vehicle_id); v != NULL; v = v->Next()) {
    00370     if (v->cargo_type == cargo) amount += v->cargo_cap;
    00371   }
    00372
    00373   return amount;
    00374 }
    00375
    00376 /* static */ int32 AIVehicle::GetCargoLoad(VehicleID vehicle_id, CargoID cargo)
    00377 {
    00378   if (!IsValidVehicle(vehicle_id)) return -1;
    00379   if (!AICargo::IsValidCargo(cargo)) return -1;
    00380
    00381   uint32 amount = 0;
    00382   for (const Vehicle *v = ::Vehicle::Get(vehicle_id); v != NULL; v = v->Next()) {
    00383     if (v->cargo_type == cargo) amount += v->cargo.Count();
    00384   }
    00385
    00386   return amount;
    00387 }
    00388
    00389 /* static */ GroupID AIVehicle::GetGroupID(VehicleID vehicle_id)
    00390 {
    00391   if (!IsValidVehicle(vehicle_id)) return AIGroup::GROUP_INVALID;
    00392
    00393   return ::Vehicle::Get(vehicle_id)->group_id;
    00394 }
    00395
    00396 /* static */ bool AIVehicle::IsArticulated(VehicleID vehicle_id)
    00397 {
    00398   if (!IsValidVehicle(vehicle_id)) return false;
    00399   if (GetVehicleType(vehicle_id) != VT_ROAD && GetVehicleType(vehicle_id) != VT_RAIL) return false;
    00400
    00401   const Vehicle *v = ::Vehicle::Get(vehicle_id);
    00402   switch (v->type) {
    00403     case VEH_ROAD: return ::RoadVehicle::From(v)->HasArticulatedPart();
    00404     case VEH_TRAIN: return ::Train::From(v)->HasArticulatedPart();
    00405     default: NOT_REACHED();
    00406   }
    00407 }
    00408
    00409 /* static */ bool AIVehicle::HasSharedOrders(VehicleID vehicle_id)
    00410 {
    00411   if (!IsValidVehicle(vehicle_id)) return false;
    00412
    00413   Vehicle *v = ::Vehicle::Get(vehicle_id);
    00414   return v->orders.list != NULL && v->orders.list->GetNumVehicles() > 1;
    00415 }
    00416
    00417 /* static */ int AIVehicle::GetReliability(VehicleID vehicle_id)
    00418 {
    00419   if (!IsValidVehicle(vehicle_id)) return -1;
    00420
    00421   const Vehicle *v = ::Vehicle::Get(vehicle_id);
    00422   return ::ToPercent16(v->reliability);
    00423 }