Codechange: Un-bitstuff commands taking a ClientID (i.e. CMD_CLIENT_ID).
[openttd-github.git] / src / script / api / script_vehicle.cpp
blob3da5739eebc6f19028d4fe1a68701d5b49fb8ed5
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 */
8 /** @file script_vehicle.cpp Implementation of ScriptVehicle. */
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 "../../roadveh_cmd.h"
24 #include "../../train_cmd.h"
25 #include "../../vehicle_cmd.h"
26 #include "table/strings.h"
28 #include "../../safeguards.h"
30 /* static */ bool ScriptVehicle::IsValidVehicle(VehicleID vehicle_id)
32 const Vehicle *v = ::Vehicle::GetIfValid(vehicle_id);
33 return v != nullptr && (v->owner == ScriptObject::GetCompany() || ScriptObject::GetCompany() == OWNER_DEITY) && (v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && ::Train::From(v)->IsFreeWagon()));
36 /* static */ ScriptCompany::CompanyID ScriptVehicle::GetOwner(VehicleID vehicle_id)
38 if (!IsValidVehicle(vehicle_id)) return ScriptCompany::COMPANY_INVALID;
40 return static_cast<ScriptCompany::CompanyID>((int)::Vehicle::Get(vehicle_id)->owner);
43 /* static */ int32 ScriptVehicle::GetNumWagons(VehicleID vehicle_id)
45 if (!IsValidVehicle(vehicle_id)) return -1;
47 int num = 1;
49 const Train *v = ::Train::GetIfValid(vehicle_id);
50 if (v != nullptr) {
51 while ((v = v->GetNextUnit()) != nullptr) num++;
54 return num;
57 /* static */ int ScriptVehicle::GetLength(VehicleID vehicle_id)
59 if (!IsValidVehicle(vehicle_id)) return -1;
61 const Vehicle *v = ::Vehicle::Get(vehicle_id);
62 return v->IsGroundVehicle() ? v->GetGroundVehicleCache()->cached_total_length : -1;
65 /* static */ VehicleID ScriptVehicle::_BuildVehicleInternal(TileIndex depot, EngineID engine_id, CargoID cargo)
67 EnforcePrecondition(VEHICLE_INVALID, ScriptObject::GetCompany() != OWNER_DEITY);
68 EnforcePrecondition(VEHICLE_INVALID, ScriptEngine::IsBuildable(engine_id));
69 EnforcePrecondition(VEHICLE_INVALID, cargo == CT_INVALID || ScriptCargo::IsValidCargo(cargo));
71 ::VehicleType type = ::Engine::Get(engine_id)->type;
73 EnforcePreconditionCustomError(VEHICLE_INVALID, !ScriptGameSettings::IsDisabledVehicleType((ScriptVehicle::VehicleType)type), ScriptVehicle::ERR_VEHICLE_BUILD_DISABLED);
75 if (!ScriptObject::Command<CMD_BUILD_VEHICLE>::Do(&ScriptInstance::DoCommandReturnVehicleID, depot, engine_id, true, cargo, INVALID_CLIENT_ID)) return VEHICLE_INVALID;
77 /* In case of test-mode, we return VehicleID 0 */
78 return 0;
81 /* static */ VehicleID ScriptVehicle::BuildVehicle(TileIndex depot, EngineID engine_id)
83 return _BuildVehicleInternal(depot, engine_id, CT_INVALID);
86 /* static */ VehicleID ScriptVehicle::BuildVehicleWithRefit(TileIndex depot, EngineID engine_id, CargoID cargo)
88 EnforcePrecondition(VEHICLE_INVALID, ScriptCargo::IsValidCargo(cargo));
89 return _BuildVehicleInternal(depot, engine_id, cargo);
92 /* static */ int ScriptVehicle::GetBuildWithRefitCapacity(TileIndex depot, EngineID engine_id, CargoID cargo)
94 if (!ScriptEngine::IsBuildable(engine_id)) return -1;
95 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
97 CommandCost res = ::Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, depot, engine_id, true, cargo, INVALID_CLIENT_ID);
98 return res.Succeeded() ? _returned_refit_capacity : -1;
101 /* static */ VehicleID ScriptVehicle::CloneVehicle(TileIndex depot, VehicleID vehicle_id, bool share_orders)
103 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
104 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
106 if (!ScriptObject::Command<CMD_CLONE_VEHICLE>::Do(&ScriptInstance::DoCommandReturnVehicleID, depot, vehicle_id, share_orders, {})) return VEHICLE_INVALID;
108 /* In case of test-mode, we return VehicleID 0 */
109 return 0;
112 /* static */ bool ScriptVehicle::_MoveWagonInternal(VehicleID source_vehicle_id, int source_wagon, bool move_attached_wagons, int dest_vehicle_id, int dest_wagon)
114 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
115 EnforcePrecondition(false, IsValidVehicle(source_vehicle_id) && source_wagon < GetNumWagons(source_vehicle_id));
116 EnforcePrecondition(false, dest_vehicle_id == -1 || (IsValidVehicle(dest_vehicle_id) && dest_wagon < GetNumWagons(dest_vehicle_id)));
117 EnforcePrecondition(false, ::Vehicle::Get(source_vehicle_id)->type == VEH_TRAIN);
118 EnforcePrecondition(false, dest_vehicle_id == -1 || ::Vehicle::Get(dest_vehicle_id)->type == VEH_TRAIN);
120 const Train *v = ::Train::Get(source_vehicle_id);
121 while (source_wagon-- > 0) v = v->GetNextUnit();
122 const Train *w = nullptr;
123 if (dest_vehicle_id != -1) {
124 w = ::Train::Get(dest_vehicle_id);
125 while (dest_wagon-- > 0) w = w->GetNextUnit();
128 return ScriptObject::Command<CMD_MOVE_RAIL_VEHICLE>::Do(0, v->index | (move_attached_wagons ? 1 : 0) << 20, w == nullptr ? ::INVALID_VEHICLE : w->index, {});
131 /* static */ bool ScriptVehicle::MoveWagon(VehicleID source_vehicle_id, int source_wagon, int dest_vehicle_id, int dest_wagon)
133 return _MoveWagonInternal(source_vehicle_id, source_wagon, false, dest_vehicle_id, dest_wagon);
136 /* static */ bool ScriptVehicle::MoveWagonChain(VehicleID source_vehicle_id, int source_wagon, int dest_vehicle_id, int dest_wagon)
138 return _MoveWagonInternal(source_vehicle_id, source_wagon, true, dest_vehicle_id, dest_wagon);
141 /* static */ int ScriptVehicle::GetRefitCapacity(VehicleID vehicle_id, CargoID cargo)
143 if (!IsValidVehicle(vehicle_id)) return -1;
144 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
146 CommandCost res = ::Command<CMD_REFIT_VEHICLE>::Do(DC_QUERY_COST, 0, vehicle_id, cargo, {});
147 return res.Succeeded() ? _returned_refit_capacity : -1;
150 /* static */ bool ScriptVehicle::RefitVehicle(VehicleID vehicle_id, CargoID cargo)
152 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
153 EnforcePrecondition(false, IsValidVehicle(vehicle_id) && ScriptCargo::IsValidCargo(cargo));
155 return ScriptObject::Command<CMD_REFIT_VEHICLE>::Do(0, vehicle_id, cargo, {});
159 /* static */ bool ScriptVehicle::SellVehicle(VehicleID vehicle_id)
161 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
162 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
164 const Vehicle *v = ::Vehicle::Get(vehicle_id);
165 return ScriptObject::Command<CMD_SELL_VEHICLE>::Do(0, vehicle_id, v->type == VEH_TRAIN, false, INVALID_CLIENT_ID);
168 /* static */ bool ScriptVehicle::_SellWagonInternal(VehicleID vehicle_id, int wagon, bool sell_attached_wagons)
170 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
171 EnforcePrecondition(false, IsValidVehicle(vehicle_id) && wagon < GetNumWagons(vehicle_id));
172 EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
174 const Train *v = ::Train::Get(vehicle_id);
175 while (wagon-- > 0) v = v->GetNextUnit();
177 return ScriptObject::Command<CMD_SELL_VEHICLE>::Do(0, v->index, sell_attached_wagons, false, INVALID_CLIENT_ID);
180 /* static */ bool ScriptVehicle::SellWagon(VehicleID vehicle_id, int wagon)
182 return _SellWagonInternal(vehicle_id, wagon, false);
185 /* static */ bool ScriptVehicle::SellWagonChain(VehicleID vehicle_id, int wagon)
187 return _SellWagonInternal(vehicle_id, wagon, true);
190 /* static */ bool ScriptVehicle::SendVehicleToDepot(VehicleID vehicle_id)
192 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
193 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
195 return ScriptObject::Command<CMD_SEND_VEHICLE_TO_DEPOT>::Do(0, vehicle_id, 0, {});
198 /* static */ bool ScriptVehicle::SendVehicleToDepotForServicing(VehicleID vehicle_id)
200 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
201 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
203 return ScriptObject::Command<CMD_SEND_VEHICLE_TO_DEPOT>::Do(0, vehicle_id | DEPOT_SERVICE, 0, {});
206 /* static */ bool ScriptVehicle::IsInDepot(VehicleID vehicle_id)
208 if (!IsValidVehicle(vehicle_id)) return false;
209 return ::Vehicle::Get(vehicle_id)->IsChainInDepot();
212 /* static */ bool ScriptVehicle::IsStoppedInDepot(VehicleID vehicle_id)
214 if (!IsValidVehicle(vehicle_id)) return false;
215 return ::Vehicle::Get(vehicle_id)->IsStoppedInDepot();
218 /* static */ bool ScriptVehicle::StartStopVehicle(VehicleID vehicle_id)
220 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
221 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
223 return ScriptObject::Command<CMD_START_STOP_VEHICLE>::Do(0, vehicle_id, 0, {});
226 /* static */ bool ScriptVehicle::ReverseVehicle(VehicleID vehicle_id)
228 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
229 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
230 EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_ROAD || ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
232 switch (::Vehicle::Get(vehicle_id)->type) {
233 case VEH_ROAD: return ScriptObject::Command<CMD_TURN_ROADVEH>::Do(0, vehicle_id, 0, {});
234 case VEH_TRAIN: return ScriptObject::Command<CMD_REVERSE_TRAIN_DIRECTION>::Do(0, vehicle_id, 0, {});
235 default: NOT_REACHED();
239 /* static */ bool ScriptVehicle::SetName(VehicleID vehicle_id, Text *name)
241 CCountedPtr<Text> counter(name);
243 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
244 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
245 EnforcePrecondition(false, name != nullptr);
246 const char *text = name->GetDecodedText();
247 EnforcePreconditionEncodedText(false, text);
248 EnforcePreconditionCustomError(false, ::Utf8StringLength(text) < MAX_LENGTH_VEHICLE_NAME_CHARS, ScriptError::ERR_PRECONDITION_STRING_TOO_LONG);
250 return ScriptObject::Command<CMD_RENAME_VEHICLE>::Do(0, vehicle_id, 0, text);
253 /* static */ TileIndex ScriptVehicle::GetLocation(VehicleID vehicle_id)
255 if (!IsValidVehicle(vehicle_id)) return INVALID_TILE;
257 const Vehicle *v = ::Vehicle::Get(vehicle_id);
258 if (v->type == VEH_AIRCRAFT) {
259 uint x = Clamp(v->x_pos / TILE_SIZE, 0, ::MapSizeX() - 2);
260 uint y = Clamp(v->y_pos / TILE_SIZE, 0, ::MapSizeY() - 2);
261 return ::TileXY(x, y);
264 return v->tile;
267 /* static */ EngineID ScriptVehicle::GetEngineType(VehicleID vehicle_id)
269 if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
271 return ::Vehicle::Get(vehicle_id)->engine_type;
274 /* static */ EngineID ScriptVehicle::GetWagonEngineType(VehicleID vehicle_id, int wagon)
276 if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
277 if (wagon >= GetNumWagons(vehicle_id)) return INVALID_ENGINE;
279 const Vehicle *v = ::Vehicle::Get(vehicle_id);
280 if (v->type == VEH_TRAIN) {
281 while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
283 return v->engine_type;
286 /* static */ int32 ScriptVehicle::GetUnitNumber(VehicleID vehicle_id)
288 if (!IsValidVehicle(vehicle_id)) return -1;
290 return ::Vehicle::Get(vehicle_id)->unitnumber;
293 /* static */ char *ScriptVehicle::GetName(VehicleID vehicle_id)
295 if (!IsValidVehicle(vehicle_id)) return nullptr;
297 ::SetDParam(0, vehicle_id);
298 return GetString(STR_VEHICLE_NAME);
301 /* static */ int32 ScriptVehicle::GetAge(VehicleID vehicle_id)
303 if (!IsValidVehicle(vehicle_id)) return -1;
305 return ::Vehicle::Get(vehicle_id)->age;
308 /* static */ int32 ScriptVehicle::GetWagonAge(VehicleID vehicle_id, int wagon)
310 if (!IsValidVehicle(vehicle_id)) return -1;
311 if (wagon >= GetNumWagons(vehicle_id)) return -1;
313 const Vehicle *v = ::Vehicle::Get(vehicle_id);
314 if (v->type == VEH_TRAIN) {
315 while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
317 return v->age;
320 /* static */ int32 ScriptVehicle::GetMaxAge(VehicleID vehicle_id)
322 if (!IsValidVehicle(vehicle_id)) return -1;
324 return ::Vehicle::Get(vehicle_id)->max_age;
327 /* static */ int32 ScriptVehicle::GetAgeLeft(VehicleID vehicle_id)
329 if (!IsValidVehicle(vehicle_id)) return -1;
331 return ::Vehicle::Get(vehicle_id)->max_age - ::Vehicle::Get(vehicle_id)->age;
334 /* static */ int32 ScriptVehicle::GetCurrentSpeed(VehicleID vehicle_id)
336 if (!IsValidVehicle(vehicle_id)) return -1;
338 const ::Vehicle *v = ::Vehicle::Get(vehicle_id);
339 return (v->vehstatus & (::VS_STOPPED | ::VS_CRASHED)) == 0 ? v->GetDisplaySpeed() : 0; // km-ish/h
342 /* static */ ScriptVehicle::VehicleState ScriptVehicle::GetState(VehicleID vehicle_id)
344 if (!IsValidVehicle(vehicle_id)) return ScriptVehicle::VS_INVALID;
346 const Vehicle *v = ::Vehicle::Get(vehicle_id);
347 byte vehstatus = v->vehstatus;
349 if (vehstatus & ::VS_CRASHED) return ScriptVehicle::VS_CRASHED;
350 if (v->breakdown_ctr != 0) return ScriptVehicle::VS_BROKEN;
351 if (v->IsStoppedInDepot()) return ScriptVehicle::VS_IN_DEPOT;
352 if (vehstatus & ::VS_STOPPED) return ScriptVehicle::VS_STOPPED;
353 if (v->current_order.IsType(OT_LOADING)) return ScriptVehicle::VS_AT_STATION;
354 return ScriptVehicle::VS_RUNNING;
357 /* static */ Money ScriptVehicle::GetRunningCost(VehicleID vehicle_id)
359 if (!IsValidVehicle(vehicle_id)) return -1;
361 return ::Vehicle::Get(vehicle_id)->GetRunningCost() >> 8;
364 /* static */ Money ScriptVehicle::GetProfitThisYear(VehicleID vehicle_id)
366 if (!IsValidVehicle(vehicle_id)) return -1;
368 return ::Vehicle::Get(vehicle_id)->GetDisplayProfitThisYear();
371 /* static */ Money ScriptVehicle::GetProfitLastYear(VehicleID vehicle_id)
373 if (!IsValidVehicle(vehicle_id)) return -1;
375 return ::Vehicle::Get(vehicle_id)->GetDisplayProfitLastYear();
378 /* static */ Money ScriptVehicle::GetCurrentValue(VehicleID vehicle_id)
380 if (!IsValidVehicle(vehicle_id)) return -1;
382 return ::Vehicle::Get(vehicle_id)->value;
385 /* static */ ScriptVehicle::VehicleType ScriptVehicle::GetVehicleType(VehicleID vehicle_id)
387 if (!IsValidVehicle(vehicle_id)) return VT_INVALID;
389 switch (::Vehicle::Get(vehicle_id)->type) {
390 case VEH_ROAD: return VT_ROAD;
391 case VEH_TRAIN: return VT_RAIL;
392 case VEH_SHIP: return VT_WATER;
393 case VEH_AIRCRAFT: return VT_AIR;
394 default: return VT_INVALID;
398 /* static */ ScriptRoad::RoadType ScriptVehicle::GetRoadType(VehicleID vehicle_id)
400 if (!IsValidVehicle(vehicle_id)) return ScriptRoad::ROADTYPE_INVALID;
401 if (GetVehicleType(vehicle_id) != VT_ROAD) return ScriptRoad::ROADTYPE_INVALID;
403 return (ScriptRoad::RoadType)(int)(::RoadVehicle::Get(vehicle_id))->roadtype;
406 /* static */ int32 ScriptVehicle::GetCapacity(VehicleID vehicle_id, CargoID cargo)
408 if (!IsValidVehicle(vehicle_id)) return -1;
409 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
411 uint32 amount = 0;
412 for (const Vehicle *v = ::Vehicle::Get(vehicle_id); v != nullptr; v = v->Next()) {
413 if (v->cargo_type == cargo) amount += v->cargo_cap;
416 return amount;
419 /* static */ int32 ScriptVehicle::GetCargoLoad(VehicleID vehicle_id, CargoID cargo)
421 if (!IsValidVehicle(vehicle_id)) return -1;
422 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
424 uint32 amount = 0;
425 for (const Vehicle *v = ::Vehicle::Get(vehicle_id); v != nullptr; v = v->Next()) {
426 if (v->cargo_type == cargo) amount += v->cargo.StoredCount();
429 return amount;
432 /* static */ GroupID ScriptVehicle::GetGroupID(VehicleID vehicle_id)
434 if (!IsValidVehicle(vehicle_id)) return ScriptGroup::GROUP_INVALID;
436 return ::Vehicle::Get(vehicle_id)->group_id;
439 /* static */ bool ScriptVehicle::IsArticulated(VehicleID vehicle_id)
441 if (!IsValidVehicle(vehicle_id)) return false;
442 if (GetVehicleType(vehicle_id) != VT_ROAD && GetVehicleType(vehicle_id) != VT_RAIL) return false;
444 const Vehicle *v = ::Vehicle::Get(vehicle_id);
445 switch (v->type) {
446 case VEH_ROAD: return ::RoadVehicle::From(v)->HasArticulatedPart();
447 case VEH_TRAIN: return ::Train::From(v)->HasArticulatedPart();
448 default: NOT_REACHED();
452 /* static */ bool ScriptVehicle::HasSharedOrders(VehicleID vehicle_id)
454 if (!IsValidVehicle(vehicle_id)) return false;
456 Vehicle *v = ::Vehicle::Get(vehicle_id);
457 return v->orders.list != nullptr && v->orders.list->GetNumVehicles() > 1;
460 /* static */ int ScriptVehicle::GetReliability(VehicleID vehicle_id)
462 if (!IsValidVehicle(vehicle_id)) return -1;
464 const Vehicle *v = ::Vehicle::Get(vehicle_id);
465 return ::ToPercent16(v->reliability);
468 /* static */ uint ScriptVehicle::GetMaximumOrderDistance(VehicleID vehicle_id)
470 if (!IsValidVehicle(vehicle_id)) return 0;
472 const ::Vehicle *v = ::Vehicle::Get(vehicle_id);
473 switch (v->type) {
474 case VEH_AIRCRAFT:
475 return ::Aircraft::From(v)->acache.cached_max_range_sqr;
477 default:
478 return 0;