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/>.
8 /** @file script_engine.cpp Implementation of ScriptEngine. */
10 #include "../../stdafx.h"
11 #include "script_engine.hpp"
12 #include "script_cargo.hpp"
13 #include "../../company_base.h"
14 #include "../../strings_func.h"
15 #include "../../rail.h"
16 #include "../../road.h"
17 #include "../../engine_base.h"
18 #include "../../engine_func.h"
19 #include "../../articulated_vehicles.h"
20 #include "../../engine_cmd.h"
21 #include "../../timer/timer_game_calendar.h"
22 #include "table/strings.h"
24 #include "../../safeguards.h"
26 /* static */ bool ScriptEngine::IsValidEngine(EngineID engine_id
)
28 EnforceDeityOrCompanyModeValid(false);
29 const Engine
*e
= ::Engine::GetIfValid(engine_id
);
30 if (e
== nullptr || !e
->IsEnabled()) return false;
32 /* AIs have only access to engines they can purchase or still have in use.
33 * Deity has access to all engined that will be or were available ever. */
34 CompanyID company
= ScriptObject::GetCompany();
35 return ScriptCompanyMode::IsDeity() || ::IsEngineBuildable(engine_id
, e
->type
, company
) || ::Company::Get(company
)->group_all
[e
->type
].GetNumEngines(engine_id
) > 0;
38 /* static */ bool ScriptEngine::IsBuildable(EngineID engine_id
)
40 EnforceDeityOrCompanyModeValid(false);
41 const Engine
*e
= ::Engine::GetIfValid(engine_id
);
42 return e
!= nullptr && ::IsEngineBuildable(engine_id
, e
->type
, ScriptObject::GetCompany());
45 /* static */ std::optional
<std::string
> ScriptEngine::GetName(EngineID engine_id
)
47 if (!IsValidEngine(engine_id
)) return std::nullopt
;
49 ::SetDParam(0, engine_id
);
50 return GetString(STR_ENGINE_NAME
);
53 /* static */ CargoID
ScriptEngine::GetCargoType(EngineID engine_id
)
55 if (!IsValidEngine(engine_id
)) return INVALID_CARGO
;
57 CargoArray cap
= ::GetCapacityOfArticulatedParts(engine_id
);
59 auto it
= std::max_element(std::cbegin(cap
), std::cend(cap
));
60 if (*it
== 0) return INVALID_CARGO
;
62 return CargoID(std::distance(std::cbegin(cap
), it
));
65 /* static */ bool ScriptEngine::CanRefitCargo(EngineID engine_id
, CargoID cargo_id
)
67 if (!IsValidEngine(engine_id
)) return false;
68 if (!ScriptCargo::IsValidCargo(cargo_id
)) return false;
70 return HasBit(::GetUnionOfArticulatedRefitMasks(engine_id
, true), cargo_id
);
73 /* static */ bool ScriptEngine::CanPullCargo(EngineID engine_id
, CargoID cargo_id
)
75 if (!IsValidEngine(engine_id
)) return false;
76 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_RAIL
) return false;
77 if (!ScriptCargo::IsValidCargo(cargo_id
)) return false;
79 return (::RailVehInfo(engine_id
)->ai_passenger_only
!= 1) || ScriptCargo::HasCargoClass(cargo_id
, ScriptCargo::CC_PASSENGERS
);
83 /* static */ SQInteger
ScriptEngine::GetCapacity(EngineID engine_id
)
85 if (!IsValidEngine(engine_id
)) return -1;
87 const Engine
*e
= ::Engine::Get(engine_id
);
91 CargoArray capacities
= GetCapacityOfArticulatedParts(engine_id
);
92 for (uint
&cap
: capacities
) {
93 if (cap
!= 0) return cap
;
100 return e
->GetDisplayDefaultCapacity();
102 default: NOT_REACHED();
106 /* static */ SQInteger
ScriptEngine::GetReliability(EngineID engine_id
)
108 if (!IsValidEngine(engine_id
)) return -1;
109 if (GetVehicleType(engine_id
) == ScriptVehicle::VT_RAIL
&& IsWagon(engine_id
)) return -1;
111 return ::ToPercent16(::Engine::Get(engine_id
)->reliability
);
114 /* static */ SQInteger
ScriptEngine::GetMaxSpeed(EngineID engine_id
)
116 if (!IsValidEngine(engine_id
)) return -1;
118 const Engine
*e
= ::Engine::Get(engine_id
);
119 uint max_speed
= e
->GetDisplayMaxSpeed(); // km-ish/h
120 if (e
->type
== VEH_AIRCRAFT
) max_speed
/= _settings_game
.vehicle
.plane_speed
;
124 /* static */ Money
ScriptEngine::GetPrice(EngineID engine_id
)
126 if (!IsValidEngine(engine_id
)) return -1;
128 return ::Engine::Get(engine_id
)->GetCost();
131 /* static */ SQInteger
ScriptEngine::GetMaxAge(EngineID engine_id
)
133 if (!IsValidEngine(engine_id
)) return -1;
134 if (GetVehicleType(engine_id
) == ScriptVehicle::VT_RAIL
&& IsWagon(engine_id
)) return -1;
136 return ::Engine::Get(engine_id
)->GetLifeLengthInDays().base();
139 /* static */ Money
ScriptEngine::GetRunningCost(EngineID engine_id
)
141 if (!IsValidEngine(engine_id
)) return -1;
143 return ::Engine::Get(engine_id
)->GetRunningCost();
146 /* static */ SQInteger
ScriptEngine::GetPower(EngineID engine_id
)
148 if (!IsValidEngine(engine_id
)) return -1;
149 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_RAIL
&& GetVehicleType(engine_id
) != ScriptVehicle::VT_ROAD
) return -1;
150 if (IsWagon(engine_id
)) return -1;
152 return ::Engine::Get(engine_id
)->GetPower();
155 /* static */ SQInteger
ScriptEngine::GetWeight(EngineID engine_id
)
157 if (!IsValidEngine(engine_id
)) return -1;
158 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_RAIL
&& GetVehicleType(engine_id
) != ScriptVehicle::VT_ROAD
) return -1;
160 return ::Engine::Get(engine_id
)->GetDisplayWeight();
163 /* static */ SQInteger
ScriptEngine::GetMaxTractiveEffort(EngineID engine_id
)
165 if (!IsValidEngine(engine_id
)) return -1;
166 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_RAIL
&& GetVehicleType(engine_id
) != ScriptVehicle::VT_ROAD
) return -1;
167 if (IsWagon(engine_id
)) return -1;
169 return ::Engine::Get(engine_id
)->GetDisplayMaxTractiveEffort() / 1000;
172 /* static */ ScriptDate::Date
ScriptEngine::GetDesignDate(EngineID engine_id
)
174 if (!IsValidEngine(engine_id
)) return ScriptDate::DATE_INVALID
;
176 return (ScriptDate::Date
)::Engine::Get(engine_id
)->intro_date
.base();
179 /* static */ ScriptVehicle::VehicleType
ScriptEngine::GetVehicleType(EngineID engine_id
)
181 if (!IsValidEngine(engine_id
)) return ScriptVehicle::VT_INVALID
;
183 switch (::Engine::Get(engine_id
)->type
) {
184 case VEH_ROAD
: return ScriptVehicle::VT_ROAD
;
185 case VEH_TRAIN
: return ScriptVehicle::VT_RAIL
;
186 case VEH_SHIP
: return ScriptVehicle::VT_WATER
;
187 case VEH_AIRCRAFT
: return ScriptVehicle::VT_AIR
;
188 default: NOT_REACHED();
192 /* static */ bool ScriptEngine::IsWagon(EngineID engine_id
)
194 if (!IsValidEngine(engine_id
)) return false;
195 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_RAIL
) return false;
197 return ::RailVehInfo(engine_id
)->power
== 0;
200 /* static */ bool ScriptEngine::CanRunOnRail(EngineID engine_id
, ScriptRail::RailType track_rail_type
)
202 if (!IsValidEngine(engine_id
)) return false;
203 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_RAIL
) return false;
204 if (!ScriptRail::IsRailTypeAvailable(track_rail_type
)) return false;
206 return ::IsCompatibleRail((::RailType
)::RailVehInfo(engine_id
)->railtype
, (::RailType
)track_rail_type
);
209 /* static */ bool ScriptEngine::HasPowerOnRail(EngineID engine_id
, ScriptRail::RailType track_rail_type
)
211 if (!IsValidEngine(engine_id
)) return false;
212 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_RAIL
) return false;
213 if (!ScriptRail::IsRailTypeAvailable(track_rail_type
)) return false;
215 return ::HasPowerOnRail((::RailType
)::RailVehInfo(engine_id
)->railtype
, (::RailType
)track_rail_type
);
218 /* static */ bool ScriptEngine::CanRunOnRoad(EngineID engine_id
, ScriptRoad::RoadType road_type
)
220 return HasPowerOnRoad(engine_id
, road_type
);
223 /* static */ bool ScriptEngine::HasPowerOnRoad(EngineID engine_id
, ScriptRoad::RoadType road_type
)
225 if (!IsValidEngine(engine_id
)) return false;
226 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_ROAD
) return false;
227 if (!ScriptRoad::IsRoadTypeAvailable(road_type
)) return false;
229 return ::HasPowerOnRoad((::RoadType
)::RoadVehInfo(engine_id
)->roadtype
, (::RoadType
)road_type
);
232 /* static */ ScriptRoad::RoadType
ScriptEngine::GetRoadType(EngineID engine_id
)
234 if (!IsValidEngine(engine_id
)) return ScriptRoad::ROADTYPE_INVALID
;
235 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_ROAD
) return ScriptRoad::ROADTYPE_INVALID
;
237 return (ScriptRoad::RoadType
)(uint
)::RoadVehInfo(engine_id
)->roadtype
;
240 /* static */ ScriptRail::RailType
ScriptEngine::GetRailType(EngineID engine_id
)
242 if (!IsValidEngine(engine_id
)) return ScriptRail::RAILTYPE_INVALID
;
243 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_RAIL
) return ScriptRail::RAILTYPE_INVALID
;
245 return (ScriptRail::RailType
)(uint
)::RailVehInfo(engine_id
)->railtype
;
248 /* static */ bool ScriptEngine::IsArticulated(EngineID engine_id
)
250 if (!IsValidEngine(engine_id
)) return false;
251 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_ROAD
&& GetVehicleType(engine_id
) != ScriptVehicle::VT_RAIL
) return false;
253 return IsArticulatedEngine(engine_id
);
256 /* static */ ScriptAirport::PlaneType
ScriptEngine::GetPlaneType(EngineID engine_id
)
258 if (!IsValidEngine(engine_id
)) return ScriptAirport::PT_INVALID
;
259 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_AIR
) return ScriptAirport::PT_INVALID
;
261 return (ScriptAirport::PlaneType
)::AircraftVehInfo(engine_id
)->subtype
;
264 /* static */ SQInteger
ScriptEngine::GetMaximumOrderDistance(EngineID engine_id
)
266 if (!IsValidEngine(engine_id
)) return 0;
267 if (GetVehicleType(engine_id
) != ScriptVehicle::VT_AIR
) return 0;
269 return (SQInteger
)::Engine::Get(engine_id
)->GetRange() * ::Engine::Get(engine_id
)->GetRange();
272 /* static */ bool ScriptEngine::EnableForCompany(EngineID engine_id
, ScriptCompany::CompanyID company
)
274 company
= ScriptCompany::ResolveCompanyID(company
);
276 EnforceDeityMode(false);
277 EnforcePrecondition(false, IsValidEngine(engine_id
));
278 EnforcePrecondition(false, company
!= ScriptCompany::COMPANY_INVALID
);
280 return ScriptObject::Command
<CMD_ENGINE_CTRL
>::Do(engine_id
, (::CompanyID
)company
, true);
283 /* static */ bool ScriptEngine::DisableForCompany(EngineID engine_id
, ScriptCompany::CompanyID company
)
285 company
= ScriptCompany::ResolveCompanyID(company
);
287 EnforceDeityMode(false);
288 EnforcePrecondition(false, IsValidEngine(engine_id
));
289 EnforcePrecondition(false, company
!= ScriptCompany::COMPANY_INVALID
);
291 return ScriptObject::Command
<CMD_ENGINE_CTRL
>::Do(engine_id
, (::CompanyID
)company
, false);