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/>.
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
!= NULL
&& (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;
48 const Train
*v
= ::Train::GetIfValid(vehicle_id
);
50 while ((v
= v
->GetNextUnit()) != NULL
) 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::BuildVehicle(TileIndex depot
, EngineID engine_id
)
66 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
67 EnforcePrecondition(VEHICLE_INVALID
, ScriptEngine::IsBuildable(engine_id
));
69 ::VehicleType type
= ::Engine::Get(engine_id
)->type
;
71 EnforcePreconditionCustomError(VEHICLE_INVALID
, !ScriptGameSettings::IsDisabledVehicleType((ScriptVehicle::VehicleType
)type
), ScriptVehicle::ERR_VEHICLE_BUILD_DISABLED
);
73 if (!ScriptObject::DoCommand(depot
, engine_id
, 0, ::GetCmdBuildVeh(type
), NULL
, &ScriptInstance::DoCommandReturnVehicleID
)) return VEHICLE_INVALID
;
75 /* In case of test-mode, we return VehicleID 0 */
79 /* static */ VehicleID
ScriptVehicle::CloneVehicle(TileIndex depot
, VehicleID vehicle_id
, bool share_orders
)
81 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
82 EnforcePrecondition(false, IsValidVehicle(vehicle_id
));
84 if (!ScriptObject::DoCommand(depot
, vehicle_id
, share_orders
, CMD_CLONE_VEHICLE
, NULL
, &ScriptInstance::DoCommandReturnVehicleID
)) return VEHICLE_INVALID
;
86 /* In case of test-mode, we return VehicleID 0 */
90 /* static */ bool ScriptVehicle::_MoveWagonInternal(VehicleID source_vehicle_id
, int source_wagon
, bool move_attached_wagons
, int dest_vehicle_id
, int dest_wagon
)
92 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
93 EnforcePrecondition(false, IsValidVehicle(source_vehicle_id
) && source_wagon
< GetNumWagons(source_vehicle_id
));
94 EnforcePrecondition(false, dest_vehicle_id
== -1 || (IsValidVehicle(dest_vehicle_id
) && dest_wagon
< GetNumWagons(dest_vehicle_id
)));
95 EnforcePrecondition(false, ::Vehicle::Get(source_vehicle_id
)->type
== VEH_TRAIN
);
96 EnforcePrecondition(false, dest_vehicle_id
== -1 || ::Vehicle::Get(dest_vehicle_id
)->type
== VEH_TRAIN
);
98 const Train
*v
= ::Train::Get(source_vehicle_id
);
99 while (source_wagon
-- > 0) v
= v
->GetNextUnit();
100 const Train
*w
= NULL
;
101 if (dest_vehicle_id
!= -1) {
102 w
= ::Train::Get(dest_vehicle_id
);
103 while (dest_wagon
-- > 0) w
= w
->GetNextUnit();
106 return ScriptObject::DoCommand(0, v
->index
| (move_attached_wagons
? 1 : 0) << 20, w
== NULL
? ::INVALID_VEHICLE
: w
->index
, CMD_MOVE_RAIL_VEHICLE
);
109 /* static */ bool ScriptVehicle::MoveWagon(VehicleID source_vehicle_id
, int source_wagon
, int dest_vehicle_id
, int dest_wagon
)
111 return _MoveWagonInternal(source_vehicle_id
, source_wagon
, false, dest_vehicle_id
, dest_wagon
);
114 /* static */ bool ScriptVehicle::MoveWagonChain(VehicleID source_vehicle_id
, int source_wagon
, int dest_vehicle_id
, int dest_wagon
)
116 return _MoveWagonInternal(source_vehicle_id
, source_wagon
, true, dest_vehicle_id
, dest_wagon
);
119 /* static */ int ScriptVehicle::GetRefitCapacity(VehicleID vehicle_id
, CargoID cargo
)
121 if (!IsValidVehicle(vehicle_id
)) return -1;
122 if (!ScriptCargo::IsValidCargo(cargo
)) return -1;
124 CommandCost res
= ::DoCommand(0, vehicle_id
, cargo
, DC_QUERY_COST
, GetCmdRefitVeh(::Vehicle::Get(vehicle_id
)));
125 return res
.Succeeded() ? _returned_refit_capacity
: -1;
128 /* static */ bool ScriptVehicle::RefitVehicle(VehicleID vehicle_id
, CargoID cargo
)
130 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
131 EnforcePrecondition(false, IsValidVehicle(vehicle_id
) && ScriptCargo::IsValidCargo(cargo
));
133 return ScriptObject::DoCommand(0, vehicle_id
, cargo
, GetCmdRefitVeh(::Vehicle::Get(vehicle_id
)));
137 /* static */ bool ScriptVehicle::SellVehicle(VehicleID vehicle_id
)
139 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
140 EnforcePrecondition(false, IsValidVehicle(vehicle_id
));
142 const Vehicle
*v
= ::Vehicle::Get(vehicle_id
);
143 return ScriptObject::DoCommand(0, vehicle_id
| (v
->type
== VEH_TRAIN
? 1 : 0) << 20, 0, GetCmdSellVeh(v
));
146 /* static */ bool ScriptVehicle::_SellWagonInternal(VehicleID vehicle_id
, int wagon
, bool sell_attached_wagons
)
148 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
149 EnforcePrecondition(false, IsValidVehicle(vehicle_id
) && wagon
< GetNumWagons(vehicle_id
));
150 EnforcePrecondition(false, ::Vehicle::Get(vehicle_id
)->type
== VEH_TRAIN
);
152 const Train
*v
= ::Train::Get(vehicle_id
);
153 while (wagon
-- > 0) v
= v
->GetNextUnit();
155 return ScriptObject::DoCommand(0, v
->index
| (sell_attached_wagons
? 1 : 0) << 20, 0, CMD_SELL_VEHICLE
);
158 /* static */ bool ScriptVehicle::SellWagon(VehicleID vehicle_id
, int wagon
)
160 return _SellWagonInternal(vehicle_id
, wagon
, false);
163 /* static */ bool ScriptVehicle::SellWagonChain(VehicleID vehicle_id
, int wagon
)
165 return _SellWagonInternal(vehicle_id
, wagon
, true);
168 /* static */ bool ScriptVehicle::SendVehicleToDepot(VehicleID vehicle_id
)
170 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
171 EnforcePrecondition(false, IsValidVehicle(vehicle_id
));
173 return ScriptObject::DoCommand(0, vehicle_id
, 0, GetCmdSendToDepot(::Vehicle::Get(vehicle_id
)));
176 /* static */ bool ScriptVehicle::SendVehicleToDepotForServicing(VehicleID vehicle_id
)
178 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
179 EnforcePrecondition(false, IsValidVehicle(vehicle_id
));
181 return ScriptObject::DoCommand(0, vehicle_id
| DEPOT_SERVICE
, 0, GetCmdSendToDepot(::Vehicle::Get(vehicle_id
)));
184 /* static */ bool ScriptVehicle::IsInDepot(VehicleID vehicle_id
)
186 if (!IsValidVehicle(vehicle_id
)) return false;
187 return ::Vehicle::Get(vehicle_id
)->IsChainInDepot();
190 /* static */ bool ScriptVehicle::IsStoppedInDepot(VehicleID vehicle_id
)
192 if (!IsValidVehicle(vehicle_id
)) return false;
193 return ::Vehicle::Get(vehicle_id
)->IsStoppedInDepot();
196 /* static */ bool ScriptVehicle::StartStopVehicle(VehicleID vehicle_id
)
198 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
199 EnforcePrecondition(false, IsValidVehicle(vehicle_id
));
201 return ScriptObject::DoCommand(0, vehicle_id
, 0, CMD_START_STOP_VEHICLE
);
204 /* static */ bool ScriptVehicle::ReverseVehicle(VehicleID vehicle_id
)
206 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
207 EnforcePrecondition(false, IsValidVehicle(vehicle_id
));
208 EnforcePrecondition(false, ::Vehicle::Get(vehicle_id
)->type
== VEH_ROAD
|| ::Vehicle::Get(vehicle_id
)->type
== VEH_TRAIN
);
210 switch (::Vehicle::Get(vehicle_id
)->type
) {
211 case VEH_ROAD
: return ScriptObject::DoCommand(0, vehicle_id
, 0, CMD_TURN_ROADVEH
);
212 case VEH_TRAIN
: return ScriptObject::DoCommand(0, vehicle_id
, 0, CMD_REVERSE_TRAIN_DIRECTION
);
213 default: NOT_REACHED();
217 /* static */ bool ScriptVehicle::SetName(VehicleID vehicle_id
, Text
*name
)
219 CCountedPtr
<Text
> counter(name
);
221 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
222 EnforcePrecondition(false, IsValidVehicle(vehicle_id
));
223 EnforcePrecondition(false, name
!= NULL
);
224 const char *text
= name
->GetDecodedText();
225 EnforcePreconditionEncodedText(false, text
);
226 EnforcePreconditionCustomError(false, ::Utf8StringLength(text
) < MAX_LENGTH_VEHICLE_NAME_CHARS
, ScriptError::ERR_PRECONDITION_STRING_TOO_LONG
);
228 return ScriptObject::DoCommand(0, vehicle_id
, 0, CMD_RENAME_VEHICLE
, text
);
231 /* static */ TileIndex
ScriptVehicle::GetLocation(VehicleID vehicle_id
)
233 if (!IsValidVehicle(vehicle_id
)) return INVALID_TILE
;
235 const Vehicle
*v
= ::Vehicle::Get(vehicle_id
);
236 if (v
->type
== VEH_AIRCRAFT
) {
237 uint x
= Clamp(v
->x_pos
/ TILE_SIZE
, 0, ::MapSizeX() - 2);
238 uint y
= Clamp(v
->y_pos
/ TILE_SIZE
, 0, ::MapSizeY() - 2);
239 return ::TileXY(x
, y
);
245 /* static */ EngineID
ScriptVehicle::GetEngineType(VehicleID vehicle_id
)
247 if (!IsValidVehicle(vehicle_id
)) return INVALID_ENGINE
;
249 return ::Vehicle::Get(vehicle_id
)->engine_type
;
252 /* static */ EngineID
ScriptVehicle::GetWagonEngineType(VehicleID vehicle_id
, int wagon
)
254 if (!IsValidVehicle(vehicle_id
)) return INVALID_ENGINE
;
255 if (wagon
>= GetNumWagons(vehicle_id
)) return INVALID_ENGINE
;
257 const Vehicle
*v
= ::Vehicle::Get(vehicle_id
);
258 if (v
->type
== VEH_TRAIN
) {
259 while (wagon
-- > 0) v
= ::Train::From(v
)->GetNextUnit();
261 return v
->engine_type
;
264 /* static */ int32
ScriptVehicle::GetUnitNumber(VehicleID vehicle_id
)
266 if (!IsValidVehicle(vehicle_id
)) return -1;
268 return ::Vehicle::Get(vehicle_id
)->unitnumber
;
271 /* static */ char *ScriptVehicle::GetName(VehicleID vehicle_id
)
273 if (!IsValidVehicle(vehicle_id
)) return NULL
;
275 ::SetDParam(0, vehicle_id
);
276 return GetString(STR_VEHICLE_NAME
);
279 /* static */ int32
ScriptVehicle::GetAge(VehicleID vehicle_id
)
281 if (!IsValidVehicle(vehicle_id
)) return -1;
283 return ::Vehicle::Get(vehicle_id
)->age
;
286 /* static */ int32
ScriptVehicle::GetWagonAge(VehicleID vehicle_id
, int wagon
)
288 if (!IsValidVehicle(vehicle_id
)) return -1;
289 if (wagon
>= GetNumWagons(vehicle_id
)) return -1;
291 const Vehicle
*v
= ::Vehicle::Get(vehicle_id
);
292 if (v
->type
== VEH_TRAIN
) {
293 while (wagon
-- > 0) v
= ::Train::From(v
)->GetNextUnit();
298 /* static */ int32
ScriptVehicle::GetMaxAge(VehicleID vehicle_id
)
300 if (!IsValidVehicle(vehicle_id
)) return -1;
302 return ::Vehicle::Get(vehicle_id
)->max_age
;
305 /* static */ int32
ScriptVehicle::GetAgeLeft(VehicleID vehicle_id
)
307 if (!IsValidVehicle(vehicle_id
)) return -1;
309 return ::Vehicle::Get(vehicle_id
)->max_age
- ::Vehicle::Get(vehicle_id
)->age
;
312 /* static */ int32
ScriptVehicle::GetCurrentSpeed(VehicleID vehicle_id
)
314 if (!IsValidVehicle(vehicle_id
)) return -1;
316 const ::Vehicle
*v
= ::Vehicle::Get(vehicle_id
);
317 return (v
->vehstatus
& (::VS_STOPPED
| ::VS_CRASHED
)) == 0 ? v
->GetDisplaySpeed() : 0; // km-ish/h
320 /* static */ ScriptVehicle::VehicleState
ScriptVehicle::GetState(VehicleID vehicle_id
)
322 if (!IsValidVehicle(vehicle_id
)) return ScriptVehicle::VS_INVALID
;
324 const Vehicle
*v
= ::Vehicle::Get(vehicle_id
);
325 byte vehstatus
= v
->vehstatus
;
327 if (vehstatus
& ::VS_CRASHED
) return ScriptVehicle::VS_CRASHED
;
328 if (v
->breakdown_ctr
!= 0) return ScriptVehicle::VS_BROKEN
;
329 if (v
->IsStoppedInDepot()) return ScriptVehicle::VS_IN_DEPOT
;
330 if (vehstatus
& ::VS_STOPPED
) return ScriptVehicle::VS_STOPPED
;
331 if (v
->current_order
.IsType(OT_LOADING
)) return ScriptVehicle::VS_AT_STATION
;
332 return ScriptVehicle::VS_RUNNING
;
335 /* static */ Money
ScriptVehicle::GetRunningCost(VehicleID vehicle_id
)
337 if (!IsValidVehicle(vehicle_id
)) return -1;
339 return ::Vehicle::Get(vehicle_id
)->GetRunningCost() >> 8;
342 /* static */ Money
ScriptVehicle::GetProfitThisYear(VehicleID vehicle_id
)
344 if (!IsValidVehicle(vehicle_id
)) return -1;
346 return ::Vehicle::Get(vehicle_id
)->GetDisplayProfitThisYear();
349 /* static */ Money
ScriptVehicle::GetProfitLastYear(VehicleID vehicle_id
)
351 if (!IsValidVehicle(vehicle_id
)) return -1;
353 return ::Vehicle::Get(vehicle_id
)->GetDisplayProfitLastYear();
356 /* static */ Money
ScriptVehicle::GetCurrentValue(VehicleID vehicle_id
)
358 if (!IsValidVehicle(vehicle_id
)) return -1;
360 return ::Vehicle::Get(vehicle_id
)->value
;
363 /* static */ ScriptVehicle::VehicleType
ScriptVehicle::GetVehicleType(VehicleID vehicle_id
)
365 if (!IsValidVehicle(vehicle_id
)) return VT_INVALID
;
367 switch (::Vehicle::Get(vehicle_id
)->type
) {
368 case VEH_ROAD
: return VT_ROAD
;
369 case VEH_TRAIN
: return VT_RAIL
;
370 case VEH_SHIP
: return VT_WATER
;
371 case VEH_AIRCRAFT
: return VT_AIR
;
372 default: return VT_INVALID
;
376 /* static */ ScriptRoad::RoadType
ScriptVehicle::GetRoadType(VehicleID vehicle_id
)
378 if (!IsValidVehicle(vehicle_id
)) return ScriptRoad::ROADTYPE_INVALID
;
379 if (GetVehicleType(vehicle_id
) != VT_ROAD
) return ScriptRoad::ROADTYPE_INVALID
;
381 return (ScriptRoad::RoadType
)(::RoadVehicle::Get(vehicle_id
))->roadtype
;
384 /* static */ int32
ScriptVehicle::GetCapacity(VehicleID vehicle_id
, CargoID cargo
)
386 if (!IsValidVehicle(vehicle_id
)) return -1;
387 if (!ScriptCargo::IsValidCargo(cargo
)) return -1;
390 for (const Vehicle
*v
= ::Vehicle::Get(vehicle_id
); v
!= NULL
; v
= v
->Next()) {
391 if (v
->cargo_type
== cargo
) amount
+= v
->cargo_cap
;
397 /* static */ int32
ScriptVehicle::GetCargoLoad(VehicleID vehicle_id
, CargoID cargo
)
399 if (!IsValidVehicle(vehicle_id
)) return -1;
400 if (!ScriptCargo::IsValidCargo(cargo
)) return -1;
403 for (const Vehicle
*v
= ::Vehicle::Get(vehicle_id
); v
!= NULL
; v
= v
->Next()) {
404 if (v
->cargo_type
== cargo
) amount
+= v
->cargo
.StoredCount();
410 /* static */ GroupID
ScriptVehicle::GetGroupID(VehicleID vehicle_id
)
412 if (!IsValidVehicle(vehicle_id
)) return ScriptGroup::GROUP_INVALID
;
414 return ::Vehicle::Get(vehicle_id
)->group_id
;
417 /* static */ bool ScriptVehicle::IsArticulated(VehicleID vehicle_id
)
419 if (!IsValidVehicle(vehicle_id
)) return false;
420 if (GetVehicleType(vehicle_id
) != VT_ROAD
&& GetVehicleType(vehicle_id
) != VT_RAIL
) return false;
422 const Vehicle
*v
= ::Vehicle::Get(vehicle_id
);
424 case VEH_ROAD
: return ::RoadVehicle::From(v
)->HasArticulatedPart();
425 case VEH_TRAIN
: return ::Train::From(v
)->HasArticulatedPart();
426 default: NOT_REACHED();
430 /* static */ bool ScriptVehicle::HasSharedOrders(VehicleID vehicle_id
)
432 if (!IsValidVehicle(vehicle_id
)) return false;
434 Vehicle
*v
= ::Vehicle::Get(vehicle_id
);
435 return v
->orders
.list
!= NULL
&& v
->orders
.list
->GetNumVehicles() > 1;
438 /* static */ int ScriptVehicle::GetReliability(VehicleID vehicle_id
)
440 if (!IsValidVehicle(vehicle_id
)) return -1;
442 const Vehicle
*v
= ::Vehicle::Get(vehicle_id
);
443 return ::ToPercent16(v
->reliability
);
446 /* static */ uint
ScriptVehicle::GetMaximumOrderDistance(VehicleID vehicle_id
)
448 if (!IsValidVehicle(vehicle_id
)) return 0;
450 const ::Vehicle
*v
= ::Vehicle::Get(vehicle_id
);
453 return _settings_game
.pf
.pathfinder_for_ships
!= VPF_NPF
? 129 : 0;
456 return ::Aircraft::From(v
)->acache
.cached_max_range_sqr
;