Codechange: Use null pointer literal instead of the NULL macro
[openttd-github.git] / src / script / api / script_vehicle.cpp
blob15bc68a971d518270321dca3aa7ccf45532eb22c
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
8 */
10 /** @file script_vehicle.cpp Implementation of ScriptVehicle. */
12 #include "../../stdafx.h"
13 #include "script_engine.hpp"
14 #include "script_cargo.hpp"
15 #include "script_gamesettings.hpp"
16 #include "script_group.hpp"
17 #include "../script_instance.hpp"
18 #include "../../string_func.h"
19 #include "../../strings_func.h"
20 #include "../../command_func.h"
21 #include "../../roadveh.h"
22 #include "../../train.h"
23 #include "../../vehicle_func.h"
24 #include "../../aircraft.h"
25 #include "table/strings.h"
27 #include "../../safeguards.h"
29 /* static */ bool ScriptVehicle::IsValidVehicle(VehicleID vehicle_id)
31 const Vehicle *v = ::Vehicle::GetIfValid(vehicle_id);
32 return v != nullptr && (v->owner == ScriptObject::GetCompany() || ScriptObject::GetCompany() == OWNER_DEITY) && (v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && ::Train::From(v)->IsFreeWagon()));
35 /* static */ ScriptCompany::CompanyID ScriptVehicle::GetOwner(VehicleID vehicle_id)
37 if (!IsValidVehicle(vehicle_id)) return ScriptCompany::COMPANY_INVALID;
39 return static_cast<ScriptCompany::CompanyID>((int)::Vehicle::Get(vehicle_id)->owner);
42 /* static */ int32 ScriptVehicle::GetNumWagons(VehicleID vehicle_id)
44 if (!IsValidVehicle(vehicle_id)) return -1;
46 int num = 1;
48 const Train *v = ::Train::GetIfValid(vehicle_id);
49 if (v != nullptr) {
50 while ((v = v->GetNextUnit()) != nullptr) num++;
53 return num;
56 /* static */ int ScriptVehicle::GetLength(VehicleID vehicle_id)
58 if (!IsValidVehicle(vehicle_id)) return -1;
60 const Vehicle *v = ::Vehicle::Get(vehicle_id);
61 return v->IsGroundVehicle() ? v->GetGroundVehicleCache()->cached_total_length : -1;
64 /* static */ VehicleID ScriptVehicle::_BuildVehicleInternal(TileIndex depot, EngineID engine_id, CargoID cargo)
66 EnforcePrecondition(VEHICLE_INVALID, ScriptObject::GetCompany() != OWNER_DEITY);
67 EnforcePrecondition(VEHICLE_INVALID, ScriptEngine::IsBuildable(engine_id));
68 EnforcePrecondition(VEHICLE_INVALID, cargo == CT_INVALID || ScriptCargo::IsValidCargo(cargo));
70 ::VehicleType type = ::Engine::Get(engine_id)->type;
72 EnforcePreconditionCustomError(VEHICLE_INVALID, !ScriptGameSettings::IsDisabledVehicleType((ScriptVehicle::VehicleType)type), ScriptVehicle::ERR_VEHICLE_BUILD_DISABLED);
74 if (!ScriptObject::DoCommand(depot, engine_id | (cargo << 24), 0, ::GetCmdBuildVeh(type), nullptr, &ScriptInstance::DoCommandReturnVehicleID)) return VEHICLE_INVALID;
76 /* In case of test-mode, we return VehicleID 0 */
77 return 0;
80 /* static */ VehicleID ScriptVehicle::BuildVehicle(TileIndex depot, EngineID engine_id)
82 return _BuildVehicleInternal(depot, engine_id, CT_INVALID);
85 /* static */ VehicleID ScriptVehicle::BuildVehicleWithRefit(TileIndex depot, EngineID engine_id, CargoID cargo)
87 EnforcePrecondition(VEHICLE_INVALID, ScriptCargo::IsValidCargo(cargo));
88 return _BuildVehicleInternal(depot, engine_id, cargo);
91 /* static */ int ScriptVehicle::GetBuildWithRefitCapacity(TileIndex depot, EngineID engine_id, CargoID cargo)
93 if (!ScriptEngine::IsBuildable(engine_id)) return -1;
94 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
96 ::VehicleType type = ::Engine::Get(engine_id)->type;
98 CommandCost res = ::DoCommand(depot, engine_id | (cargo << 24), 0, DC_QUERY_COST, ::GetCmdBuildVeh(type));
99 return res.Succeeded() ? _returned_refit_capacity : -1;
102 /* static */ VehicleID ScriptVehicle::CloneVehicle(TileIndex depot, VehicleID vehicle_id, bool share_orders)
104 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
105 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
107 if (!ScriptObject::DoCommand(depot, vehicle_id, share_orders, CMD_CLONE_VEHICLE, nullptr, &ScriptInstance::DoCommandReturnVehicleID)) return VEHICLE_INVALID;
109 /* In case of test-mode, we return VehicleID 0 */
110 return 0;
113 /* static */ bool ScriptVehicle::_MoveWagonInternal(VehicleID source_vehicle_id, int source_wagon, bool move_attached_wagons, int dest_vehicle_id, int dest_wagon)
115 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
116 EnforcePrecondition(false, IsValidVehicle(source_vehicle_id) && source_wagon < GetNumWagons(source_vehicle_id));
117 EnforcePrecondition(false, dest_vehicle_id == -1 || (IsValidVehicle(dest_vehicle_id) && dest_wagon < GetNumWagons(dest_vehicle_id)));
118 EnforcePrecondition(false, ::Vehicle::Get(source_vehicle_id)->type == VEH_TRAIN);
119 EnforcePrecondition(false, dest_vehicle_id == -1 || ::Vehicle::Get(dest_vehicle_id)->type == VEH_TRAIN);
121 const Train *v = ::Train::Get(source_vehicle_id);
122 while (source_wagon-- > 0) v = v->GetNextUnit();
123 const Train *w = nullptr;
124 if (dest_vehicle_id != -1) {
125 w = ::Train::Get(dest_vehicle_id);
126 while (dest_wagon-- > 0) w = w->GetNextUnit();
129 return ScriptObject::DoCommand(0, v->index | (move_attached_wagons ? 1 : 0) << 20, w == nullptr ? ::INVALID_VEHICLE : w->index, CMD_MOVE_RAIL_VEHICLE);
132 /* static */ bool ScriptVehicle::MoveWagon(VehicleID source_vehicle_id, int source_wagon, int dest_vehicle_id, int dest_wagon)
134 return _MoveWagonInternal(source_vehicle_id, source_wagon, false, dest_vehicle_id, dest_wagon);
137 /* static */ bool ScriptVehicle::MoveWagonChain(VehicleID source_vehicle_id, int source_wagon, int dest_vehicle_id, int dest_wagon)
139 return _MoveWagonInternal(source_vehicle_id, source_wagon, true, dest_vehicle_id, dest_wagon);
142 /* static */ int ScriptVehicle::GetRefitCapacity(VehicleID vehicle_id, CargoID cargo)
144 if (!IsValidVehicle(vehicle_id)) return -1;
145 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
147 CommandCost res = ::DoCommand(0, vehicle_id, cargo, DC_QUERY_COST, GetCmdRefitVeh(::Vehicle::Get(vehicle_id)));
148 return res.Succeeded() ? _returned_refit_capacity : -1;
151 /* static */ bool ScriptVehicle::RefitVehicle(VehicleID vehicle_id, CargoID cargo)
153 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
154 EnforcePrecondition(false, IsValidVehicle(vehicle_id) && ScriptCargo::IsValidCargo(cargo));
156 return ScriptObject::DoCommand(0, vehicle_id, cargo, GetCmdRefitVeh(::Vehicle::Get(vehicle_id)));
160 /* static */ bool ScriptVehicle::SellVehicle(VehicleID vehicle_id)
162 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
163 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
165 const Vehicle *v = ::Vehicle::Get(vehicle_id);
166 return ScriptObject::DoCommand(0, vehicle_id | (v->type == VEH_TRAIN ? 1 : 0) << 20, 0, GetCmdSellVeh(v));
169 /* static */ bool ScriptVehicle::_SellWagonInternal(VehicleID vehicle_id, int wagon, bool sell_attached_wagons)
171 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
172 EnforcePrecondition(false, IsValidVehicle(vehicle_id) && wagon < GetNumWagons(vehicle_id));
173 EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
175 const Train *v = ::Train::Get(vehicle_id);
176 while (wagon-- > 0) v = v->GetNextUnit();
178 return ScriptObject::DoCommand(0, v->index | (sell_attached_wagons ? 1 : 0) << 20, 0, CMD_SELL_VEHICLE);
181 /* static */ bool ScriptVehicle::SellWagon(VehicleID vehicle_id, int wagon)
183 return _SellWagonInternal(vehicle_id, wagon, false);
186 /* static */ bool ScriptVehicle::SellWagonChain(VehicleID vehicle_id, int wagon)
188 return _SellWagonInternal(vehicle_id, wagon, true);
191 /* static */ bool ScriptVehicle::SendVehicleToDepot(VehicleID vehicle_id)
193 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
194 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
196 return ScriptObject::DoCommand(0, vehicle_id, 0, GetCmdSendToDepot(::Vehicle::Get(vehicle_id)));
199 /* static */ bool ScriptVehicle::SendVehicleToDepotForServicing(VehicleID vehicle_id)
201 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
202 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
204 return ScriptObject::DoCommand(0, vehicle_id | DEPOT_SERVICE, 0, GetCmdSendToDepot(::Vehicle::Get(vehicle_id)));
207 /* static */ bool ScriptVehicle::IsInDepot(VehicleID vehicle_id)
209 if (!IsValidVehicle(vehicle_id)) return false;
210 return ::Vehicle::Get(vehicle_id)->IsChainInDepot();
213 /* static */ bool ScriptVehicle::IsStoppedInDepot(VehicleID vehicle_id)
215 if (!IsValidVehicle(vehicle_id)) return false;
216 return ::Vehicle::Get(vehicle_id)->IsStoppedInDepot();
219 /* static */ bool ScriptVehicle::StartStopVehicle(VehicleID vehicle_id)
221 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
222 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
224 return ScriptObject::DoCommand(0, vehicle_id, 0, CMD_START_STOP_VEHICLE);
227 /* static */ bool ScriptVehicle::ReverseVehicle(VehicleID vehicle_id)
229 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
230 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
231 EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_ROAD || ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
233 switch (::Vehicle::Get(vehicle_id)->type) {
234 case VEH_ROAD: return ScriptObject::DoCommand(0, vehicle_id, 0, CMD_TURN_ROADVEH);
235 case VEH_TRAIN: return ScriptObject::DoCommand(0, vehicle_id, 0, CMD_REVERSE_TRAIN_DIRECTION);
236 default: NOT_REACHED();
240 /* static */ bool ScriptVehicle::SetName(VehicleID vehicle_id, Text *name)
242 CCountedPtr<Text> counter(name);
244 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
245 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
246 EnforcePrecondition(false, name != nullptr);
247 const char *text = name->GetDecodedText();
248 EnforcePreconditionEncodedText(false, text);
249 EnforcePreconditionCustomError(false, ::Utf8StringLength(text) < MAX_LENGTH_VEHICLE_NAME_CHARS, ScriptError::ERR_PRECONDITION_STRING_TOO_LONG);
251 return ScriptObject::DoCommand(0, vehicle_id, 0, CMD_RENAME_VEHICLE, text);
254 /* static */ TileIndex ScriptVehicle::GetLocation(VehicleID vehicle_id)
256 if (!IsValidVehicle(vehicle_id)) return INVALID_TILE;
258 const Vehicle *v = ::Vehicle::Get(vehicle_id);
259 if (v->type == VEH_AIRCRAFT) {
260 uint x = Clamp(v->x_pos / TILE_SIZE, 0, ::MapSizeX() - 2);
261 uint y = Clamp(v->y_pos / TILE_SIZE, 0, ::MapSizeY() - 2);
262 return ::TileXY(x, y);
265 return v->tile;
268 /* static */ EngineID ScriptVehicle::GetEngineType(VehicleID vehicle_id)
270 if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
272 return ::Vehicle::Get(vehicle_id)->engine_type;
275 /* static */ EngineID ScriptVehicle::GetWagonEngineType(VehicleID vehicle_id, int wagon)
277 if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
278 if (wagon >= GetNumWagons(vehicle_id)) return INVALID_ENGINE;
280 const Vehicle *v = ::Vehicle::Get(vehicle_id);
281 if (v->type == VEH_TRAIN) {
282 while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
284 return v->engine_type;
287 /* static */ int32 ScriptVehicle::GetUnitNumber(VehicleID vehicle_id)
289 if (!IsValidVehicle(vehicle_id)) return -1;
291 return ::Vehicle::Get(vehicle_id)->unitnumber;
294 /* static */ char *ScriptVehicle::GetName(VehicleID vehicle_id)
296 if (!IsValidVehicle(vehicle_id)) return nullptr;
298 ::SetDParam(0, vehicle_id);
299 return GetString(STR_VEHICLE_NAME);
302 /* static */ int32 ScriptVehicle::GetAge(VehicleID vehicle_id)
304 if (!IsValidVehicle(vehicle_id)) return -1;
306 return ::Vehicle::Get(vehicle_id)->age;
309 /* static */ int32 ScriptVehicle::GetWagonAge(VehicleID vehicle_id, int wagon)
311 if (!IsValidVehicle(vehicle_id)) return -1;
312 if (wagon >= GetNumWagons(vehicle_id)) return -1;
314 const Vehicle *v = ::Vehicle::Get(vehicle_id);
315 if (v->type == VEH_TRAIN) {
316 while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
318 return v->age;
321 /* static */ int32 ScriptVehicle::GetMaxAge(VehicleID vehicle_id)
323 if (!IsValidVehicle(vehicle_id)) return -1;
325 return ::Vehicle::Get(vehicle_id)->max_age;
328 /* static */ int32 ScriptVehicle::GetAgeLeft(VehicleID vehicle_id)
330 if (!IsValidVehicle(vehicle_id)) return -1;
332 return ::Vehicle::Get(vehicle_id)->max_age - ::Vehicle::Get(vehicle_id)->age;
335 /* static */ int32 ScriptVehicle::GetCurrentSpeed(VehicleID vehicle_id)
337 if (!IsValidVehicle(vehicle_id)) return -1;
339 const ::Vehicle *v = ::Vehicle::Get(vehicle_id);
340 return (v->vehstatus & (::VS_STOPPED | ::VS_CRASHED)) == 0 ? v->GetDisplaySpeed() : 0; // km-ish/h
343 /* static */ ScriptVehicle::VehicleState ScriptVehicle::GetState(VehicleID vehicle_id)
345 if (!IsValidVehicle(vehicle_id)) return ScriptVehicle::VS_INVALID;
347 const Vehicle *v = ::Vehicle::Get(vehicle_id);
348 byte vehstatus = v->vehstatus;
350 if (vehstatus & ::VS_CRASHED) return ScriptVehicle::VS_CRASHED;
351 if (v->breakdown_ctr != 0) return ScriptVehicle::VS_BROKEN;
352 if (v->IsStoppedInDepot()) return ScriptVehicle::VS_IN_DEPOT;
353 if (vehstatus & ::VS_STOPPED) return ScriptVehicle::VS_STOPPED;
354 if (v->current_order.IsType(OT_LOADING)) return ScriptVehicle::VS_AT_STATION;
355 return ScriptVehicle::VS_RUNNING;
358 /* static */ Money ScriptVehicle::GetRunningCost(VehicleID vehicle_id)
360 if (!IsValidVehicle(vehicle_id)) return -1;
362 return ::Vehicle::Get(vehicle_id)->GetRunningCost() >> 8;
365 /* static */ Money ScriptVehicle::GetProfitThisYear(VehicleID vehicle_id)
367 if (!IsValidVehicle(vehicle_id)) return -1;
369 return ::Vehicle::Get(vehicle_id)->GetDisplayProfitThisYear();
372 /* static */ Money ScriptVehicle::GetProfitLastYear(VehicleID vehicle_id)
374 if (!IsValidVehicle(vehicle_id)) return -1;
376 return ::Vehicle::Get(vehicle_id)->GetDisplayProfitLastYear();
379 /* static */ Money ScriptVehicle::GetCurrentValue(VehicleID vehicle_id)
381 if (!IsValidVehicle(vehicle_id)) return -1;
383 return ::Vehicle::Get(vehicle_id)->value;
386 /* static */ ScriptVehicle::VehicleType ScriptVehicle::GetVehicleType(VehicleID vehicle_id)
388 if (!IsValidVehicle(vehicle_id)) return VT_INVALID;
390 switch (::Vehicle::Get(vehicle_id)->type) {
391 case VEH_ROAD: return VT_ROAD;
392 case VEH_TRAIN: return VT_RAIL;
393 case VEH_SHIP: return VT_WATER;
394 case VEH_AIRCRAFT: return VT_AIR;
395 default: return VT_INVALID;
399 /* static */ ScriptRoad::RoadType ScriptVehicle::GetRoadType(VehicleID vehicle_id)
401 if (!IsValidVehicle(vehicle_id)) return ScriptRoad::ROADTYPE_INVALID;
402 if (GetVehicleType(vehicle_id) != VT_ROAD) return ScriptRoad::ROADTYPE_INVALID;
404 return (ScriptRoad::RoadType)(::RoadVehicle::Get(vehicle_id))->roadtype;
407 /* static */ int32 ScriptVehicle::GetCapacity(VehicleID vehicle_id, CargoID cargo)
409 if (!IsValidVehicle(vehicle_id)) return -1;
410 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
412 uint32 amount = 0;
413 for (const Vehicle *v = ::Vehicle::Get(vehicle_id); v != nullptr; v = v->Next()) {
414 if (v->cargo_type == cargo) amount += v->cargo_cap;
417 return amount;
420 /* static */ int32 ScriptVehicle::GetCargoLoad(VehicleID vehicle_id, CargoID cargo)
422 if (!IsValidVehicle(vehicle_id)) return -1;
423 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
425 uint32 amount = 0;
426 for (const Vehicle *v = ::Vehicle::Get(vehicle_id); v != nullptr; v = v->Next()) {
427 if (v->cargo_type == cargo) amount += v->cargo.StoredCount();
430 return amount;
433 /* static */ GroupID ScriptVehicle::GetGroupID(VehicleID vehicle_id)
435 if (!IsValidVehicle(vehicle_id)) return ScriptGroup::GROUP_INVALID;
437 return ::Vehicle::Get(vehicle_id)->group_id;
440 /* static */ bool ScriptVehicle::IsArticulated(VehicleID vehicle_id)
442 if (!IsValidVehicle(vehicle_id)) return false;
443 if (GetVehicleType(vehicle_id) != VT_ROAD && GetVehicleType(vehicle_id) != VT_RAIL) return false;
445 const Vehicle *v = ::Vehicle::Get(vehicle_id);
446 switch (v->type) {
447 case VEH_ROAD: return ::RoadVehicle::From(v)->HasArticulatedPart();
448 case VEH_TRAIN: return ::Train::From(v)->HasArticulatedPart();
449 default: NOT_REACHED();
453 /* static */ bool ScriptVehicle::HasSharedOrders(VehicleID vehicle_id)
455 if (!IsValidVehicle(vehicle_id)) return false;
457 Vehicle *v = ::Vehicle::Get(vehicle_id);
458 return v->orders.list != nullptr && v->orders.list->GetNumVehicles() > 1;
461 /* static */ int ScriptVehicle::GetReliability(VehicleID vehicle_id)
463 if (!IsValidVehicle(vehicle_id)) return -1;
465 const Vehicle *v = ::Vehicle::Get(vehicle_id);
466 return ::ToPercent16(v->reliability);
469 /* static */ uint ScriptVehicle::GetMaximumOrderDistance(VehicleID vehicle_id)
471 if (!IsValidVehicle(vehicle_id)) return 0;
473 const ::Vehicle *v = ::Vehicle::Get(vehicle_id);
474 switch (v->type) {
475 case VEH_AIRCRAFT:
476 return ::Aircraft::From(v)->acache.cached_max_range_sqr;
478 default:
479 return 0;