Update: Translations from eints
[openttd-github.git] / src / script / api / script_vehicle.cpp
blob35a6d74dda27eac7d2a07f7b34a96ef2f3c83d74
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_map.hpp"
16 #include "../script_instance.hpp"
17 #include "../../string_func.h"
18 #include "../../strings_func.h"
19 #include "../../command_func.h"
20 #include "../../roadveh.h"
21 #include "../../train.h"
22 #include "../../vehicle_func.h"
23 #include "../../aircraft.h"
24 #include "../../roadveh_cmd.h"
25 #include "../../train_cmd.h"
26 #include "../../vehicle_cmd.h"
27 #include "table/strings.h"
29 #include "../../safeguards.h"
31 /* static */ bool ScriptVehicle::IsValidVehicle(VehicleID vehicle_id)
33 EnforceDeityOrCompanyModeValid(false);
34 const Vehicle *v = ::Vehicle::GetIfValid(vehicle_id);
35 return v != nullptr && (v->owner == ScriptObject::GetCompany() || ScriptCompanyMode::IsDeity()) && (v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && ::Train::From(v)->IsFreeWagon()));
38 /* static */ bool ScriptVehicle::IsPrimaryVehicle(VehicleID vehicle_id)
40 if (!IsValidVehicle(vehicle_id)) return false;
42 return ::Vehicle::Get(vehicle_id)->IsPrimaryVehicle();
45 /* static */ ScriptCompany::CompanyID ScriptVehicle::GetOwner(VehicleID vehicle_id)
47 if (!IsValidVehicle(vehicle_id)) return ScriptCompany::COMPANY_INVALID;
49 return static_cast<ScriptCompany::CompanyID>((int)::Vehicle::Get(vehicle_id)->owner);
52 /* static */ SQInteger ScriptVehicle::GetNumWagons(VehicleID vehicle_id)
54 if (!IsValidVehicle(vehicle_id)) return -1;
56 int num = 1;
58 const Train *v = ::Train::GetIfValid(vehicle_id);
59 if (v != nullptr) {
60 while ((v = v->GetNextUnit()) != nullptr) num++;
63 return num;
66 /* static */ SQInteger ScriptVehicle::GetLength(VehicleID vehicle_id)
68 if (!IsValidVehicle(vehicle_id)) return -1;
70 const Vehicle *v = ::Vehicle::Get(vehicle_id);
71 return v->IsGroundVehicle() ? v->GetGroundVehicleCache()->cached_total_length : -1;
74 /* static */ VehicleID ScriptVehicle::_BuildVehicleInternal(TileIndex depot, EngineID engine_id, CargoID cargo)
76 EnforceCompanyModeValid(VEHICLE_INVALID);
77 EnforcePrecondition(VEHICLE_INVALID, ScriptEngine::IsBuildable(engine_id));
78 EnforcePrecondition(VEHICLE_INVALID, !::IsValidCargoID(cargo) || ScriptCargo::IsValidCargo(cargo));
80 ::VehicleType type = ::Engine::Get(engine_id)->type;
82 EnforcePreconditionCustomError(VEHICLE_INVALID, !ScriptGameSettings::IsDisabledVehicleType((ScriptVehicle::VehicleType)type), ScriptVehicle::ERR_VEHICLE_BUILD_DISABLED);
84 if (!ScriptObject::Command<CMD_BUILD_VEHICLE>::Do(&ScriptInstance::DoCommandReturnVehicleID, depot, engine_id, true, cargo, INVALID_CLIENT_ID)) return VEHICLE_INVALID;
86 /* In case of test-mode, we return VehicleID 0 */
87 return 0;
90 /* static */ VehicleID ScriptVehicle::BuildVehicle(TileIndex depot, EngineID engine_id)
92 return _BuildVehicleInternal(depot, engine_id, INVALID_CARGO);
95 /* static */ VehicleID ScriptVehicle::BuildVehicleWithRefit(TileIndex depot, EngineID engine_id, CargoID cargo)
97 EnforcePrecondition(VEHICLE_INVALID, ScriptCargo::IsValidCargo(cargo));
98 return _BuildVehicleInternal(depot, engine_id, cargo);
101 /* static */ SQInteger ScriptVehicle::GetBuildWithRefitCapacity(TileIndex depot, EngineID engine_id, CargoID cargo)
103 if (!ScriptEngine::IsBuildable(engine_id)) return -1;
104 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
106 auto [res, veh_id, refit_capacity, refit_mail, cargo_capacities] = ::Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, depot, engine_id, true, cargo, INVALID_CLIENT_ID);
107 return res.Succeeded() ? refit_capacity : -1;
110 /* static */ VehicleID ScriptVehicle::CloneVehicle(TileIndex depot, VehicleID vehicle_id, bool share_orders)
112 EnforceCompanyModeValid(false);
113 EnforcePrecondition(false, IsPrimaryVehicle(vehicle_id));
115 if (!ScriptObject::Command<CMD_CLONE_VEHICLE>::Do(&ScriptInstance::DoCommandReturnVehicleID, depot, vehicle_id, share_orders)) return VEHICLE_INVALID;
117 /* In case of test-mode, we return VehicleID 0 */
118 return 0;
121 /* static */ bool ScriptVehicle::_MoveWagonInternal(VehicleID source_vehicle_id, SQInteger source_wagon, bool move_attached_wagons, SQInteger dest_vehicle_id, SQInteger dest_wagon)
123 EnforceCompanyModeValid(false);
124 EnforcePrecondition(false, IsValidVehicle(source_vehicle_id) && source_wagon < GetNumWagons(source_vehicle_id));
125 EnforcePrecondition(false, dest_vehicle_id == -1 || (IsValidVehicle(dest_vehicle_id) && dest_wagon < GetNumWagons(dest_vehicle_id)));
126 EnforcePrecondition(false, ::Vehicle::Get(source_vehicle_id)->type == VEH_TRAIN);
127 EnforcePrecondition(false, dest_vehicle_id == -1 || ::Vehicle::Get(dest_vehicle_id)->type == VEH_TRAIN);
129 const Train *v = ::Train::Get(source_vehicle_id);
130 while (source_wagon-- > 0) v = v->GetNextUnit();
131 const Train *w = nullptr;
132 if (dest_vehicle_id != -1) {
133 w = ::Train::Get(dest_vehicle_id);
134 while (dest_wagon-- > 0) w = w->GetNextUnit();
137 return ScriptObject::Command<CMD_MOVE_RAIL_VEHICLE>::Do(v->index, w == nullptr ? ::INVALID_VEHICLE : w->index, move_attached_wagons);
140 /* static */ bool ScriptVehicle::MoveWagon(VehicleID source_vehicle_id, SQInteger source_wagon, SQInteger dest_vehicle_id, SQInteger dest_wagon)
142 return _MoveWagonInternal(source_vehicle_id, source_wagon, false, dest_vehicle_id, dest_wagon);
145 /* static */ bool ScriptVehicle::MoveWagonChain(VehicleID source_vehicle_id, SQInteger source_wagon, SQInteger dest_vehicle_id, SQInteger dest_wagon)
147 return _MoveWagonInternal(source_vehicle_id, source_wagon, true, dest_vehicle_id, dest_wagon);
150 /* static */ SQInteger ScriptVehicle::GetRefitCapacity(VehicleID vehicle_id, CargoID cargo)
152 if (!IsValidVehicle(vehicle_id)) return -1;
153 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
155 auto [res, refit_capacity, refit_mail, cargo_capacities] = ::Command<CMD_REFIT_VEHICLE>::Do(DC_QUERY_COST, vehicle_id, cargo, 0, false, false, 0);
156 return res.Succeeded() ? refit_capacity : -1;
159 /* static */ bool ScriptVehicle::RefitVehicle(VehicleID vehicle_id, CargoID cargo)
161 EnforceCompanyModeValid(false);
162 EnforcePrecondition(false, IsValidVehicle(vehicle_id) && ScriptCargo::IsValidCargo(cargo));
164 return ScriptObject::Command<CMD_REFIT_VEHICLE>::Do(vehicle_id, cargo, 0, false, false, 0);
168 /* static */ bool ScriptVehicle::SellVehicle(VehicleID vehicle_id)
170 EnforceCompanyModeValid(false);
171 EnforcePrecondition(false, IsValidVehicle(vehicle_id));
173 const Vehicle *v = ::Vehicle::Get(vehicle_id);
174 return ScriptObject::Command<CMD_SELL_VEHICLE>::Do(vehicle_id, v->type == VEH_TRAIN, false, INVALID_CLIENT_ID);
177 /* static */ bool ScriptVehicle::_SellWagonInternal(VehicleID vehicle_id, SQInteger wagon, bool sell_attached_wagons)
179 EnforceCompanyModeValid(false);
180 EnforcePrecondition(false, IsValidVehicle(vehicle_id) && wagon < GetNumWagons(vehicle_id));
181 EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
183 const Train *v = ::Train::Get(vehicle_id);
184 while (wagon-- > 0) v = v->GetNextUnit();
186 return ScriptObject::Command<CMD_SELL_VEHICLE>::Do(v->index, sell_attached_wagons, false, INVALID_CLIENT_ID);
189 /* static */ bool ScriptVehicle::SellWagon(VehicleID vehicle_id, SQInteger wagon)
191 return _SellWagonInternal(vehicle_id, wagon, false);
194 /* static */ bool ScriptVehicle::SellWagonChain(VehicleID vehicle_id, SQInteger wagon)
196 return _SellWagonInternal(vehicle_id, wagon, true);
199 /* static */ bool ScriptVehicle::SendVehicleToDepot(VehicleID vehicle_id)
201 EnforceCompanyModeValid(false);
202 EnforcePrecondition(false, IsPrimaryVehicle(vehicle_id));
204 return ScriptObject::Command<CMD_SEND_VEHICLE_TO_DEPOT>::Do(vehicle_id, DepotCommand::None, {});
207 /* static */ bool ScriptVehicle::SendVehicleToDepotForServicing(VehicleID vehicle_id)
209 EnforceCompanyModeValid(false);
210 EnforcePrecondition(false, IsPrimaryVehicle(vehicle_id));
212 return ScriptObject::Command<CMD_SEND_VEHICLE_TO_DEPOT>::Do(vehicle_id, DepotCommand::Service, {});
215 /* static */ bool ScriptVehicle::IsInDepot(VehicleID vehicle_id)
217 if (!IsValidVehicle(vehicle_id)) return false;
218 return ::Vehicle::Get(vehicle_id)->IsChainInDepot();
221 /* static */ bool ScriptVehicle::IsStoppedInDepot(VehicleID vehicle_id)
223 if (!IsValidVehicle(vehicle_id)) return false;
224 return ::Vehicle::Get(vehicle_id)->IsStoppedInDepot();
227 /* static */ bool ScriptVehicle::StartStopVehicle(VehicleID vehicle_id)
229 EnforceCompanyModeValid(false);
230 EnforcePrecondition(false, IsPrimaryVehicle(vehicle_id));
232 return ScriptObject::Command<CMD_START_STOP_VEHICLE>::Do(vehicle_id, false);
235 /* static */ bool ScriptVehicle::ReverseVehicle(VehicleID vehicle_id)
237 EnforceCompanyModeValid(false);
238 EnforcePrecondition(false, IsPrimaryVehicle(vehicle_id));
239 EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_ROAD || ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
241 switch (::Vehicle::Get(vehicle_id)->type) {
242 case VEH_ROAD: return ScriptObject::Command<CMD_TURN_ROADVEH>::Do(vehicle_id);
243 case VEH_TRAIN: return ScriptObject::Command<CMD_REVERSE_TRAIN_DIRECTION>::Do(vehicle_id, false);
244 default: NOT_REACHED();
248 /* static */ bool ScriptVehicle::SetName(VehicleID vehicle_id, Text *name)
250 CCountedPtr<Text> counter(name);
252 EnforceCompanyModeValid(false);
253 EnforcePrecondition(false, IsPrimaryVehicle(vehicle_id));
254 EnforcePrecondition(false, name != nullptr);
255 const std::string &text = name->GetDecodedText();
256 EnforcePreconditionEncodedText(false, text);
257 EnforcePreconditionCustomError(false, ::Utf8StringLength(text) < MAX_LENGTH_VEHICLE_NAME_CHARS, ScriptError::ERR_PRECONDITION_STRING_TOO_LONG);
259 return ScriptObject::Command<CMD_RENAME_VEHICLE>::Do(vehicle_id, text);
262 /* static */ TileIndex ScriptVehicle::GetLocation(VehicleID vehicle_id)
264 if (!IsValidVehicle(vehicle_id)) return INVALID_TILE;
266 const Vehicle *v = ::Vehicle::Get(vehicle_id);
267 if (v->type == VEH_AIRCRAFT) {
268 uint x = Clamp(v->x_pos / TILE_SIZE, 0, ScriptMap::GetMapSizeX() - 2);
269 uint y = Clamp(v->y_pos / TILE_SIZE, 0, ScriptMap::GetMapSizeY() - 2);
270 return ::TileXY(x, y);
273 return v->tile;
276 /* static */ EngineID ScriptVehicle::GetEngineType(VehicleID vehicle_id)
278 if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
280 return ::Vehicle::Get(vehicle_id)->engine_type;
283 /* static */ EngineID ScriptVehicle::GetWagonEngineType(VehicleID vehicle_id, SQInteger wagon)
285 if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
286 if (wagon >= GetNumWagons(vehicle_id)) return INVALID_ENGINE;
288 const Vehicle *v = ::Vehicle::Get(vehicle_id);
289 if (v->type == VEH_TRAIN) {
290 while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
292 return v->engine_type;
295 /* static */ SQInteger ScriptVehicle::GetUnitNumber(VehicleID vehicle_id)
297 if (!IsPrimaryVehicle(vehicle_id)) return -1;
299 return ::Vehicle::Get(vehicle_id)->unitnumber;
302 /* static */ std::optional<std::string> ScriptVehicle::GetName(VehicleID vehicle_id)
304 if (!IsPrimaryVehicle(vehicle_id)) return std::nullopt;
306 ::SetDParam(0, vehicle_id);
307 return GetString(STR_VEHICLE_NAME);
310 /* static */ SQInteger ScriptVehicle::GetAge(VehicleID vehicle_id)
312 if (!IsValidVehicle(vehicle_id)) return -1;
314 return ::Vehicle::Get(vehicle_id)->age.base();
317 /* static */ SQInteger ScriptVehicle::GetWagonAge(VehicleID vehicle_id, SQInteger wagon)
319 if (!IsValidVehicle(vehicle_id)) return -1;
320 if (wagon >= GetNumWagons(vehicle_id)) return -1;
322 const Vehicle *v = ::Vehicle::Get(vehicle_id);
323 if (v->type == VEH_TRAIN) {
324 while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
326 return v->age.base();
329 /* static */ SQInteger ScriptVehicle::GetMaxAge(VehicleID vehicle_id)
331 if (!IsPrimaryVehicle(vehicle_id)) return -1;
333 return ::Vehicle::Get(vehicle_id)->max_age.base();
336 /* static */ SQInteger ScriptVehicle::GetAgeLeft(VehicleID vehicle_id)
338 if (!IsPrimaryVehicle(vehicle_id)) return -1;
340 return (::Vehicle::Get(vehicle_id)->max_age - ::Vehicle::Get(vehicle_id)->age).base();
343 /* static */ SQInteger ScriptVehicle::GetCurrentSpeed(VehicleID vehicle_id)
345 if (!IsPrimaryVehicle(vehicle_id)) return -1;
347 const ::Vehicle *v = ::Vehicle::Get(vehicle_id);
348 return (v->vehstatus & (::VS_STOPPED | ::VS_CRASHED)) == 0 ? v->GetDisplaySpeed() : 0; // km-ish/h
351 /* static */ ScriptVehicle::VehicleState ScriptVehicle::GetState(VehicleID vehicle_id)
353 if (!IsValidVehicle(vehicle_id)) return ScriptVehicle::VS_INVALID;
355 const Vehicle *v = ::Vehicle::Get(vehicle_id);
356 uint8_t vehstatus = v->vehstatus;
358 if (vehstatus & ::VS_CRASHED) return ScriptVehicle::VS_CRASHED;
359 if (v->breakdown_ctr != 0) return ScriptVehicle::VS_BROKEN;
360 if (v->IsStoppedInDepot()) return ScriptVehicle::VS_IN_DEPOT;
361 if (vehstatus & ::VS_STOPPED) return ScriptVehicle::VS_STOPPED;
362 if (v->current_order.IsType(OT_LOADING)) return ScriptVehicle::VS_AT_STATION;
363 return ScriptVehicle::VS_RUNNING;
366 /* static */ Money ScriptVehicle::GetRunningCost(VehicleID vehicle_id)
368 if (!IsPrimaryVehicle(vehicle_id)) return -1;
370 return ::Vehicle::Get(vehicle_id)->GetRunningCost() >> 8;
373 /* static */ Money ScriptVehicle::GetProfitThisYear(VehicleID vehicle_id)
375 if (!IsPrimaryVehicle(vehicle_id)) return -1;
377 return ::Vehicle::Get(vehicle_id)->GetDisplayProfitThisYear();
380 /* static */ Money ScriptVehicle::GetProfitLastYear(VehicleID vehicle_id)
382 if (!IsPrimaryVehicle(vehicle_id)) return -1;
384 return ::Vehicle::Get(vehicle_id)->GetDisplayProfitLastYear();
387 /* static */ Money ScriptVehicle::GetCurrentValue(VehicleID vehicle_id)
389 if (!IsValidVehicle(vehicle_id)) return -1;
391 return ::Vehicle::Get(vehicle_id)->value;
394 /* static */ ScriptVehicle::VehicleType ScriptVehicle::GetVehicleType(VehicleID vehicle_id)
396 if (!IsValidVehicle(vehicle_id)) return VT_INVALID;
398 switch (::Vehicle::Get(vehicle_id)->type) {
399 case VEH_ROAD: return VT_ROAD;
400 case VEH_TRAIN: return VT_RAIL;
401 case VEH_SHIP: return VT_WATER;
402 case VEH_AIRCRAFT: return VT_AIR;
403 default: return VT_INVALID;
407 /* static */ ScriptRoad::RoadType ScriptVehicle::GetRoadType(VehicleID vehicle_id)
409 if (!IsValidVehicle(vehicle_id)) return ScriptRoad::ROADTYPE_INVALID;
410 if (GetVehicleType(vehicle_id) != VT_ROAD) return ScriptRoad::ROADTYPE_INVALID;
412 return (ScriptRoad::RoadType)(int)(::RoadVehicle::Get(vehicle_id))->roadtype;
415 /* static */ SQInteger ScriptVehicle::GetCapacity(VehicleID vehicle_id, CargoID cargo)
417 if (!IsValidVehicle(vehicle_id)) return -1;
418 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
420 uint32_t amount = 0;
421 for (const Vehicle *v = ::Vehicle::Get(vehicle_id); v != nullptr; v = v->Next()) {
422 if (v->cargo_type == cargo) amount += v->cargo_cap;
425 return amount;
428 /* static */ SQInteger ScriptVehicle::GetCargoLoad(VehicleID vehicle_id, CargoID cargo)
430 if (!IsValidVehicle(vehicle_id)) return -1;
431 if (!ScriptCargo::IsValidCargo(cargo)) return -1;
433 uint32_t amount = 0;
434 for (const Vehicle *v = ::Vehicle::Get(vehicle_id); v != nullptr; v = v->Next()) {
435 if (v->cargo_type == cargo) amount += v->cargo.StoredCount();
438 return amount;
441 /* static */ GroupID ScriptVehicle::GetGroupID(VehicleID vehicle_id)
443 if (!IsPrimaryVehicle(vehicle_id)) return ScriptGroup::GROUP_INVALID;
445 return ::Vehicle::Get(vehicle_id)->group_id;
448 /* static */ bool ScriptVehicle::IsArticulated(VehicleID vehicle_id)
450 if (!IsValidVehicle(vehicle_id)) return false;
451 if (GetVehicleType(vehicle_id) != VT_ROAD && GetVehicleType(vehicle_id) != VT_RAIL) return false;
453 const Vehicle *v = ::Vehicle::Get(vehicle_id);
454 switch (v->type) {
455 case VEH_ROAD: return ::RoadVehicle::From(v)->HasArticulatedPart();
456 case VEH_TRAIN: return ::Train::From(v)->HasArticulatedPart();
457 default: NOT_REACHED();
461 /* static */ bool ScriptVehicle::HasSharedOrders(VehicleID vehicle_id)
463 if (!IsPrimaryVehicle(vehicle_id)) return false;
465 const Vehicle *v = ::Vehicle::Get(vehicle_id);
466 return v->orders != nullptr && v->orders->GetNumVehicles() > 1;
469 /* static */ SQInteger ScriptVehicle::GetReliability(VehicleID vehicle_id)
471 if (!IsPrimaryVehicle(vehicle_id)) return -1;
473 const Vehicle *v = ::Vehicle::Get(vehicle_id);
474 return ::ToPercent16(v->reliability);
477 /* static */ SQInteger ScriptVehicle::GetMaximumOrderDistance(VehicleID vehicle_id)
479 if (!IsPrimaryVehicle(vehicle_id)) return 0;
481 const ::Vehicle *v = ::Vehicle::Get(vehicle_id);
482 if (v->type != VEH_AIRCRAFT) return 0;
483 return ::Aircraft::From(v)->acache.cached_max_range_sqr;