1 /*
2  * This file is part of OpenTTD.
3  * 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.
4  * 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.
5  * 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/>.
6  */
7 
8 /** @file script_vehicle.cpp Implementation of ScriptVehicle. */
9 
10 #include "../../stdafx.h"
11 #include "script_engine.hpp"
12 #include "script_cargo.hpp"
13 #include "script_gamesettings.hpp"
14 #include "script_group.hpp"
15 #include "../script_instance.hpp"
16 #include "../../string_func.h"
17 #include "../../strings_func.h"
18 #include "../../command_func.h"
19 #include "../../roadveh.h"
20 #include "../../train.h"
21 #include "../../vehicle_func.h"
22 #include "../../aircraft.h"
23 #include "table/strings.h"
24 
25 #include "../../safeguards.h"
26 
IsValidVehicle(VehicleID vehicle_id)27 /* static */ bool ScriptVehicle::IsValidVehicle(VehicleID vehicle_id)
28 {
29 	const Vehicle *v = ::Vehicle::GetIfValid(vehicle_id);
30 	return v != nullptr && (v->owner == ScriptObject::GetCompany() || ScriptObject::GetCompany() == OWNER_DEITY) && (v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && ::Train::From(v)->IsFreeWagon()));
31 }
32 
GetOwner(VehicleID vehicle_id)33 /* static */ ScriptCompany::CompanyID ScriptVehicle::GetOwner(VehicleID vehicle_id)
34 {
35 	if (!IsValidVehicle(vehicle_id)) return ScriptCompany::COMPANY_INVALID;
36 
37 	return static_cast<ScriptCompany::CompanyID>((int)::Vehicle::Get(vehicle_id)->owner);
38 }
39 
GetNumWagons(VehicleID vehicle_id)40 /* static */ int32 ScriptVehicle::GetNumWagons(VehicleID vehicle_id)
41 {
42 	if (!IsValidVehicle(vehicle_id)) return -1;
43 
44 	int num = 1;
45 
46 	const Train *v = ::Train::GetIfValid(vehicle_id);
47 	if (v != nullptr) {
48 		while ((v = v->GetNextUnit()) != nullptr) num++;
49 	}
50 
51 	return num;
52 }
53 
GetLength(VehicleID vehicle_id)54 /* static */ int ScriptVehicle::GetLength(VehicleID vehicle_id)
55 {
56 	if (!IsValidVehicle(vehicle_id)) return -1;
57 
58 	const Vehicle *v = ::Vehicle::Get(vehicle_id);
59 	return v->IsGroundVehicle() ? v->GetGroundVehicleCache()->cached_total_length : -1;
60 }
61 
_BuildVehicleInternal(TileIndex depot,EngineID engine_id,CargoID cargo)62 /* static */ VehicleID ScriptVehicle::_BuildVehicleInternal(TileIndex depot, EngineID engine_id, CargoID cargo)
63 {
64 	EnforcePrecondition(VEHICLE_INVALID, ScriptObject::GetCompany() != OWNER_DEITY);
65 	EnforcePrecondition(VEHICLE_INVALID, ScriptEngine::IsBuildable(engine_id));
66 	EnforcePrecondition(VEHICLE_INVALID, cargo == CT_INVALID || ScriptCargo::IsValidCargo(cargo));
67 
68 	::VehicleType type = ::Engine::Get(engine_id)->type;
69 
70 	EnforcePreconditionCustomError(VEHICLE_INVALID, !ScriptGameSettings::IsDisabledVehicleType((ScriptVehicle::VehicleType)type), ScriptVehicle::ERR_VEHICLE_BUILD_DISABLED);
71 
72 	if (!ScriptObject::DoCommand(depot, engine_id | (cargo << 24), 0, ::GetCmdBuildVeh(type), nullptr, &ScriptInstance::DoCommandReturnVehicleID)) return VEHICLE_INVALID;
73 
74 	/* In case of test-mode, we return VehicleID 0 */
75 	return 0;
76 }
77 
BuildVehicle(TileIndex depot,EngineID engine_id)78 /* static */ VehicleID ScriptVehicle::BuildVehicle(TileIndex depot, EngineID engine_id)
79 {
80 	return _BuildVehicleInternal(depot, engine_id, CT_INVALID);
81 }
82 
BuildVehicleWithRefit(TileIndex depot,EngineID engine_id,CargoID cargo)83 /* static */ VehicleID ScriptVehicle::BuildVehicleWithRefit(TileIndex depot, EngineID engine_id, CargoID cargo)
84 {
85 	EnforcePrecondition(VEHICLE_INVALID, ScriptCargo::IsValidCargo(cargo));
86 	return _BuildVehicleInternal(depot, engine_id, cargo);
87 }
88 
GetBuildWithRefitCapacity(TileIndex depot,EngineID engine_id,CargoID cargo)89 /* static */ int ScriptVehicle::GetBuildWithRefitCapacity(TileIndex depot, EngineID engine_id, CargoID cargo)
90 {
91 	if (!ScriptEngine::IsBuildable(engine_id)) return -1;
92 	if (!ScriptCargo::IsValidCargo(cargo)) return -1;
93 
94 	::VehicleType type = ::Engine::Get(engine_id)->type;
95 
96 	CommandCost res = ::DoCommand(depot, engine_id | (cargo << 24), 0, DC_QUERY_COST, ::GetCmdBuildVeh(type));
97 	return res.Succeeded() ? _returned_refit_capacity : -1;
98 }
99 
CloneVehicle(TileIndex depot,VehicleID vehicle_id,bool share_orders)100 /* static */ VehicleID ScriptVehicle::CloneVehicle(TileIndex depot, VehicleID vehicle_id, bool share_orders)
101 {
102 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
103 	EnforcePrecondition(false, IsValidVehicle(vehicle_id));
104 
105 	if (!ScriptObject::DoCommand(depot, vehicle_id, share_orders, CMD_CLONE_VEHICLE, nullptr, &ScriptInstance::DoCommandReturnVehicleID)) return VEHICLE_INVALID;
106 
107 	/* In case of test-mode, we return VehicleID 0 */
108 	return 0;
109 }
110 
_MoveWagonInternal(VehicleID source_vehicle_id,int source_wagon,bool move_attached_wagons,int dest_vehicle_id,int dest_wagon)111 /* static */ bool ScriptVehicle::_MoveWagonInternal(VehicleID source_vehicle_id, int source_wagon, bool move_attached_wagons, int dest_vehicle_id, int dest_wagon)
112 {
113 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
114 	EnforcePrecondition(false, IsValidVehicle(source_vehicle_id) && source_wagon < GetNumWagons(source_vehicle_id));
115 	EnforcePrecondition(false, dest_vehicle_id == -1 || (IsValidVehicle(dest_vehicle_id) && dest_wagon < GetNumWagons(dest_vehicle_id)));
116 	EnforcePrecondition(false, ::Vehicle::Get(source_vehicle_id)->type == VEH_TRAIN);
117 	EnforcePrecondition(false, dest_vehicle_id == -1 || ::Vehicle::Get(dest_vehicle_id)->type == VEH_TRAIN);
118 
119 	const Train *v = ::Train::Get(source_vehicle_id);
120 	while (source_wagon-- > 0) v = v->GetNextUnit();
121 	const Train *w = nullptr;
122 	if (dest_vehicle_id != -1) {
123 		w = ::Train::Get(dest_vehicle_id);
124 		while (dest_wagon-- > 0) w = w->GetNextUnit();
125 	}
126 
127 	return ScriptObject::DoCommand(0, v->index | (move_attached_wagons ? 1 : 0) << 20, w == nullptr ? ::INVALID_VEHICLE : w->index, CMD_MOVE_RAIL_VEHICLE);
128 }
129 
MoveWagon(VehicleID source_vehicle_id,int source_wagon,int dest_vehicle_id,int dest_wagon)130 /* static */ bool ScriptVehicle::MoveWagon(VehicleID source_vehicle_id, int source_wagon, int dest_vehicle_id, int dest_wagon)
131 {
132 	return _MoveWagonInternal(source_vehicle_id, source_wagon, false, dest_vehicle_id, dest_wagon);
133 }
134 
MoveWagonChain(VehicleID source_vehicle_id,int source_wagon,int dest_vehicle_id,int dest_wagon)135 /* static */ bool ScriptVehicle::MoveWagonChain(VehicleID source_vehicle_id, int source_wagon, int dest_vehicle_id, int dest_wagon)
136 {
137 	return _MoveWagonInternal(source_vehicle_id, source_wagon, true, dest_vehicle_id, dest_wagon);
138 }
139 
GetRefitCapacity(VehicleID vehicle_id,CargoID cargo)140 /* static */ int ScriptVehicle::GetRefitCapacity(VehicleID vehicle_id, CargoID cargo)
141 {
142 	if (!IsValidVehicle(vehicle_id)) return -1;
143 	if (!ScriptCargo::IsValidCargo(cargo)) return -1;
144 
145 	CommandCost res = ::DoCommand(0, vehicle_id, cargo, DC_QUERY_COST, GetCmdRefitVeh(::Vehicle::Get(vehicle_id)));
146 	return res.Succeeded() ? _returned_refit_capacity : -1;
147 }
148 
RefitVehicle(VehicleID vehicle_id,CargoID cargo)149 /* static */ bool ScriptVehicle::RefitVehicle(VehicleID vehicle_id, CargoID cargo)
150 {
151 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
152 	EnforcePrecondition(false, IsValidVehicle(vehicle_id) && ScriptCargo::IsValidCargo(cargo));
153 
154 	return ScriptObject::DoCommand(0, vehicle_id, cargo, GetCmdRefitVeh(::Vehicle::Get(vehicle_id)));
155 }
156 
157 
SellVehicle(VehicleID vehicle_id)158 /* static */ bool ScriptVehicle::SellVehicle(VehicleID vehicle_id)
159 {
160 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
161 	EnforcePrecondition(false, IsValidVehicle(vehicle_id));
162 
163 	const Vehicle *v = ::Vehicle::Get(vehicle_id);
164 	return ScriptObject::DoCommand(0, vehicle_id | (v->type == VEH_TRAIN ? 1 : 0) << 20, 0, GetCmdSellVeh(v));
165 }
166 
_SellWagonInternal(VehicleID vehicle_id,int wagon,bool sell_attached_wagons)167 /* static */ bool ScriptVehicle::_SellWagonInternal(VehicleID vehicle_id, int wagon, bool sell_attached_wagons)
168 {
169 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
170 	EnforcePrecondition(false, IsValidVehicle(vehicle_id) && wagon < GetNumWagons(vehicle_id));
171 	EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
172 
173 	const Train *v = ::Train::Get(vehicle_id);
174 	while (wagon-- > 0) v = v->GetNextUnit();
175 
176 	return ScriptObject::DoCommand(0, v->index | (sell_attached_wagons ? 1 : 0) << 20, 0, CMD_SELL_VEHICLE);
177 }
178 
SellWagon(VehicleID vehicle_id,int wagon)179 /* static */ bool ScriptVehicle::SellWagon(VehicleID vehicle_id, int wagon)
180 {
181 	return _SellWagonInternal(vehicle_id, wagon, false);
182 }
183 
SellWagonChain(VehicleID vehicle_id,int wagon)184 /* static */ bool ScriptVehicle::SellWagonChain(VehicleID vehicle_id, int wagon)
185 {
186 	return _SellWagonInternal(vehicle_id, wagon, true);
187 }
188 
SendVehicleToDepot(VehicleID vehicle_id)189 /* static */ bool ScriptVehicle::SendVehicleToDepot(VehicleID vehicle_id)
190 {
191 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
192 	EnforcePrecondition(false, IsValidVehicle(vehicle_id));
193 
194 	return ScriptObject::DoCommand(0, vehicle_id, 0, GetCmdSendToDepot(::Vehicle::Get(vehicle_id)));
195 }
196 
SendVehicleToDepotForServicing(VehicleID vehicle_id)197 /* static */ bool ScriptVehicle::SendVehicleToDepotForServicing(VehicleID vehicle_id)
198 {
199 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
200 	EnforcePrecondition(false, IsValidVehicle(vehicle_id));
201 
202 	return ScriptObject::DoCommand(0, vehicle_id | DEPOT_SERVICE, 0, GetCmdSendToDepot(::Vehicle::Get(vehicle_id)));
203 }
204 
IsInDepot(VehicleID vehicle_id)205 /* static */ bool ScriptVehicle::IsInDepot(VehicleID vehicle_id)
206 {
207 	if (!IsValidVehicle(vehicle_id)) return false;
208 	return ::Vehicle::Get(vehicle_id)->IsChainInDepot();
209 }
210 
IsStoppedInDepot(VehicleID vehicle_id)211 /* static */ bool ScriptVehicle::IsStoppedInDepot(VehicleID vehicle_id)
212 {
213 	if (!IsValidVehicle(vehicle_id)) return false;
214 	return ::Vehicle::Get(vehicle_id)->IsStoppedInDepot();
215 }
216 
StartStopVehicle(VehicleID vehicle_id)217 /* static */ bool ScriptVehicle::StartStopVehicle(VehicleID vehicle_id)
218 {
219 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
220 	EnforcePrecondition(false, IsValidVehicle(vehicle_id));
221 
222 	return ScriptObject::DoCommand(0, vehicle_id, 0, CMD_START_STOP_VEHICLE);
223 }
224 
ReverseVehicle(VehicleID vehicle_id)225 /* static */ bool ScriptVehicle::ReverseVehicle(VehicleID vehicle_id)
226 {
227 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
228 	EnforcePrecondition(false, IsValidVehicle(vehicle_id));
229 	EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_ROAD || ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
230 
231 	switch (::Vehicle::Get(vehicle_id)->type) {
232 		case VEH_ROAD: return ScriptObject::DoCommand(0, vehicle_id, 0, CMD_TURN_ROADVEH);
233 		case VEH_TRAIN: return ScriptObject::DoCommand(0, vehicle_id, 0, CMD_REVERSE_TRAIN_DIRECTION);
234 		default: NOT_REACHED();
235 	}
236 }
237 
SetName(VehicleID vehicle_id,Text * name)238 /* static */ bool ScriptVehicle::SetName(VehicleID vehicle_id, Text *name)
239 {
240 	CCountedPtr<Text> counter(name);
241 
242 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
243 	EnforcePrecondition(false, IsValidVehicle(vehicle_id));
244 	EnforcePrecondition(false, name != nullptr);
245 	const char *text = name->GetDecodedText();
246 	EnforcePreconditionEncodedText(false, text);
247 	EnforcePreconditionCustomError(false, ::Utf8StringLength(text) < MAX_LENGTH_VEHICLE_NAME_CHARS, ScriptError::ERR_PRECONDITION_STRING_TOO_LONG);
248 
249 	return ScriptObject::DoCommand(0, vehicle_id, 0, CMD_RENAME_VEHICLE, text);
250 }
251 
GetLocation(VehicleID vehicle_id)252 /* static */ TileIndex ScriptVehicle::GetLocation(VehicleID vehicle_id)
253 {
254 	if (!IsValidVehicle(vehicle_id)) return INVALID_TILE;
255 
256 	const Vehicle *v = ::Vehicle::Get(vehicle_id);
257 	if (v->type == VEH_AIRCRAFT) {
258 		uint x = Clamp(v->x_pos / TILE_SIZE, 0, ::MapSizeX() - 2);
259 		uint y = Clamp(v->y_pos / TILE_SIZE, 0, ::MapSizeY() - 2);
260 		return ::TileXY(x, y);
261 	}
262 
263 	return v->tile;
264 }
265 
GetEngineType(VehicleID vehicle_id)266 /* static */ EngineID ScriptVehicle::GetEngineType(VehicleID vehicle_id)
267 {
268 	if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
269 
270 	return ::Vehicle::Get(vehicle_id)->engine_type;
271 }
272 
GetWagonEngineType(VehicleID vehicle_id,int wagon)273 /* static */ EngineID ScriptVehicle::GetWagonEngineType(VehicleID vehicle_id, int wagon)
274 {
275 	if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
276 	if (wagon >= GetNumWagons(vehicle_id)) return INVALID_ENGINE;
277 
278 	const Vehicle *v = ::Vehicle::Get(vehicle_id);
279 	if (v->type == VEH_TRAIN) {
280 		while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
281 	}
282 	return v->engine_type;
283 }
284 
GetUnitNumber(VehicleID vehicle_id)285 /* static */ int32 ScriptVehicle::GetUnitNumber(VehicleID vehicle_id)
286 {
287 	if (!IsValidVehicle(vehicle_id)) return -1;
288 
289 	return ::Vehicle::Get(vehicle_id)->unitnumber;
290 }
291 
GetName(VehicleID vehicle_id)292 /* static */ char *ScriptVehicle::GetName(VehicleID vehicle_id)
293 {
294 	if (!IsValidVehicle(vehicle_id)) return nullptr;
295 
296 	::SetDParam(0, vehicle_id);
297 	return GetString(STR_VEHICLE_NAME);
298 }
299 
GetAge(VehicleID vehicle_id)300 /* static */ int32 ScriptVehicle::GetAge(VehicleID vehicle_id)
301 {
302 	if (!IsValidVehicle(vehicle_id)) return -1;
303 
304 	return ::Vehicle::Get(vehicle_id)->age;
305 }
306 
GetWagonAge(VehicleID vehicle_id,int wagon)307 /* static */ int32 ScriptVehicle::GetWagonAge(VehicleID vehicle_id, int wagon)
308 {
309 	if (!IsValidVehicle(vehicle_id)) return -1;
310 	if (wagon >= GetNumWagons(vehicle_id)) return -1;
311 
312 	const Vehicle *v = ::Vehicle::Get(vehicle_id);
313 	if (v->type == VEH_TRAIN) {
314 		while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
315 	}
316 	return v->age;
317 }
318 
GetMaxAge(VehicleID vehicle_id)319 /* static */ int32 ScriptVehicle::GetMaxAge(VehicleID vehicle_id)
320 {
321 	if (!IsValidVehicle(vehicle_id)) return -1;
322 
323 	return ::Vehicle::Get(vehicle_id)->max_age;
324 }
325 
GetAgeLeft(VehicleID vehicle_id)326 /* static */ int32 ScriptVehicle::GetAgeLeft(VehicleID vehicle_id)
327 {
328 	if (!IsValidVehicle(vehicle_id)) return -1;
329 
330 	return ::Vehicle::Get(vehicle_id)->max_age - ::Vehicle::Get(vehicle_id)->age;
331 }
332 
GetCurrentSpeed(VehicleID vehicle_id)333 /* static */ int32 ScriptVehicle::GetCurrentSpeed(VehicleID vehicle_id)
334 {
335 	if (!IsValidVehicle(vehicle_id)) return -1;
336 
337 	const ::Vehicle *v = ::Vehicle::Get(vehicle_id);
338 	return (v->vehstatus & (::VS_STOPPED | ::VS_CRASHED)) == 0 ? v->GetDisplaySpeed() : 0; // km-ish/h
339 }
340 
GetState(VehicleID vehicle_id)341 /* static */ ScriptVehicle::VehicleState ScriptVehicle::GetState(VehicleID vehicle_id)
342 {
343 	if (!IsValidVehicle(vehicle_id)) return ScriptVehicle::VS_INVALID;
344 
345 	const Vehicle *v = ::Vehicle::Get(vehicle_id);
346 	byte vehstatus = v->vehstatus;
347 
348 	if (vehstatus & ::VS_CRASHED) return ScriptVehicle::VS_CRASHED;
349 	if (v->breakdown_ctr != 0) return ScriptVehicle::VS_BROKEN;
350 	if (v->IsStoppedInDepot()) return ScriptVehicle::VS_IN_DEPOT;
351 	if (vehstatus & ::VS_STOPPED) return ScriptVehicle::VS_STOPPED;
352 	if (v->current_order.IsType(OT_LOADING)) return ScriptVehicle::VS_AT_STATION;
353 	return ScriptVehicle::VS_RUNNING;
354 }
355 
GetRunningCost(VehicleID vehicle_id)356 /* static */ Money ScriptVehicle::GetRunningCost(VehicleID vehicle_id)
357 {
358 	if (!IsValidVehicle(vehicle_id)) return -1;
359 
360 	return ::Vehicle::Get(vehicle_id)->GetRunningCost() >> 8;
361 }
362 
GetProfitThisYear(VehicleID vehicle_id)363 /* static */ Money ScriptVehicle::GetProfitThisYear(VehicleID vehicle_id)
364 {
365 	if (!IsValidVehicle(vehicle_id)) return -1;
366 
367 	return ::Vehicle::Get(vehicle_id)->GetDisplayProfitThisYear();
368 }
369 
GetProfitLastYear(VehicleID vehicle_id)370 /* static */ Money ScriptVehicle::GetProfitLastYear(VehicleID vehicle_id)
371 {
372 	if (!IsValidVehicle(vehicle_id)) return -1;
373 
374 	return ::Vehicle::Get(vehicle_id)->GetDisplayProfitLastYear();
375 }
376 
GetCurrentValue(VehicleID vehicle_id)377 /* static */ Money ScriptVehicle::GetCurrentValue(VehicleID vehicle_id)
378 {
379 	if (!IsValidVehicle(vehicle_id)) return -1;
380 
381 	return ::Vehicle::Get(vehicle_id)->value;
382 }
383 
GetVehicleType(VehicleID vehicle_id)384 /* static */ ScriptVehicle::VehicleType ScriptVehicle::GetVehicleType(VehicleID vehicle_id)
385 {
386 	if (!IsValidVehicle(vehicle_id)) return VT_INVALID;
387 
388 	switch (::Vehicle::Get(vehicle_id)->type) {
389 		case VEH_ROAD:     return VT_ROAD;
390 		case VEH_TRAIN:    return VT_RAIL;
391 		case VEH_SHIP:     return VT_WATER;
392 		case VEH_AIRCRAFT: return VT_AIR;
393 		default:           return VT_INVALID;
394 	}
395 }
396 
GetRoadType(VehicleID vehicle_id)397 /* static */ ScriptRoad::RoadType ScriptVehicle::GetRoadType(VehicleID vehicle_id)
398 {
399 	if (!IsValidVehicle(vehicle_id)) return ScriptRoad::ROADTYPE_INVALID;
400 	if (GetVehicleType(vehicle_id) != VT_ROAD) return ScriptRoad::ROADTYPE_INVALID;
401 
402 	return (ScriptRoad::RoadType)(int)(::RoadVehicle::Get(vehicle_id))->roadtype;
403 }
404 
GetCapacity(VehicleID vehicle_id,CargoID cargo)405 /* static */ int32 ScriptVehicle::GetCapacity(VehicleID vehicle_id, CargoID cargo)
406 {
407 	if (!IsValidVehicle(vehicle_id)) return -1;
408 	if (!ScriptCargo::IsValidCargo(cargo)) return -1;
409 
410 	uint32 amount = 0;
411 	for (const Vehicle *v = ::Vehicle::Get(vehicle_id); v != nullptr; v = v->Next()) {
412 		if (v->cargo_type == cargo) amount += v->cargo_cap;
413 	}
414 
415 	return amount;
416 }
417 
GetCargoLoad(VehicleID vehicle_id,CargoID cargo)418 /* static */ int32 ScriptVehicle::GetCargoLoad(VehicleID vehicle_id, CargoID cargo)
419 {
420 	if (!IsValidVehicle(vehicle_id)) return -1;
421 	if (!ScriptCargo::IsValidCargo(cargo)) return -1;
422 
423 	uint32 amount = 0;
424 	for (const Vehicle *v = ::Vehicle::Get(vehicle_id); v != nullptr; v = v->Next()) {
425 		if (v->cargo_type == cargo) amount += v->cargo.StoredCount();
426 	}
427 
428 	return amount;
429 }
430 
GetGroupID(VehicleID vehicle_id)431 /* static */ GroupID ScriptVehicle::GetGroupID(VehicleID vehicle_id)
432 {
433 	if (!IsValidVehicle(vehicle_id)) return ScriptGroup::GROUP_INVALID;
434 
435 	return ::Vehicle::Get(vehicle_id)->group_id;
436 }
437 
IsArticulated(VehicleID vehicle_id)438 /* static */ bool ScriptVehicle::IsArticulated(VehicleID vehicle_id)
439 {
440 	if (!IsValidVehicle(vehicle_id)) return false;
441 	if (GetVehicleType(vehicle_id) != VT_ROAD && GetVehicleType(vehicle_id) != VT_RAIL) return false;
442 
443 	const Vehicle *v = ::Vehicle::Get(vehicle_id);
444 	switch (v->type) {
445 		case VEH_ROAD: return ::RoadVehicle::From(v)->HasArticulatedPart();
446 		case VEH_TRAIN: return ::Train::From(v)->HasArticulatedPart();
447 		default: NOT_REACHED();
448 	}
449 }
450 
HasSharedOrders(VehicleID vehicle_id)451 /* static */ bool ScriptVehicle::HasSharedOrders(VehicleID vehicle_id)
452 {
453 	if (!IsValidVehicle(vehicle_id)) return false;
454 
455 	Vehicle *v = ::Vehicle::Get(vehicle_id);
456 	return v->orders.list != nullptr && v->orders.list->GetNumVehicles() > 1;
457 }
458 
GetReliability(VehicleID vehicle_id)459 /* static */ int ScriptVehicle::GetReliability(VehicleID vehicle_id)
460 {
461 	if (!IsValidVehicle(vehicle_id)) return -1;
462 
463 	const Vehicle *v = ::Vehicle::Get(vehicle_id);
464 	return ::ToPercent16(v->reliability);
465 }
466 
GetMaximumOrderDistance(VehicleID vehicle_id)467 /* static */ uint ScriptVehicle::GetMaximumOrderDistance(VehicleID vehicle_id)
468 {
469 	if (!IsValidVehicle(vehicle_id)) return 0;
470 
471 	const ::Vehicle *v = ::Vehicle::Get(vehicle_id);
472 	switch (v->type) {
473 		case VEH_AIRCRAFT:
474 			return ::Aircraft::From(v)->acache.cached_max_range_sqr;
475 
476 		default:
477 			return 0;
478 	}
479 }
480