1use serde::{Deserialize, Serialize};
2use serde_repr::{Deserialize_repr, Serialize_repr};
3
4use crate::message::{Coupling, MessageMeta, MessageType};
5
6#[derive(Debug, thiserror::Error)]
7pub enum VehicleError {
8 #[error("vehicle not found")]
9 VehicleNotFound = 256,
10 #[error("bogie not found")]
11 BogieNotFound = 512,
12 #[error("axle not found")]
13 AxleNotFound = 1024,
14 #[error("coupling not found")]
15 CouplingNotFound = 2048,
16 #[error("pantograph not found")]
17 PantographNotFound = 4096,
18 #[error("road axle not found")]
19 RoadAxleNotFound = 8192,
20 #[error("road wheel not found")]
21 RoadWheelNotFound = 16384,
22 #[error("unknown error")]
23 Unknown = 0,
24}
25
26impl From<u32> for VehicleError {
27 fn from(value: u32) -> Self {
28 match value {
29 256 => VehicleError::VehicleNotFound,
30 512 => VehicleError::BogieNotFound,
31 1024 => VehicleError::AxleNotFound,
32 2048 => VehicleError::CouplingNotFound,
33 4096 => VehicleError::PantographNotFound,
34 8192 => VehicleError::RoadAxleNotFound,
35 16384 => VehicleError::RoadWheelNotFound,
36 _ => VehicleError::Unknown,
37 }
38 }
39}
40
41#[cfg(feature = "ffi")]
42pub fn spawned_inverted_to_train() -> bool {
44 unsafe { lotus_script_sys::vehicle::spawned_inverted_to_train() == 1 }
45}
46
47#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53pub struct TrainConfigurationChanged {
54 pub entity_id: u64,
55 pub reversed_to_train: bool,
56 pub index_in_train: usize,
57 pub train_vehicle_count: usize,
58}
59
60impl MessageType for TrainConfigurationChanged {
61 const MESSAGE_META: MessageMeta = MessageMeta::new("builtin", "vehicle_in_train_changed", None);
62}
63
64impl TrainConfigurationChanged {
67 pub fn neighbour_vehicle_count(&self, coupling: Coupling) -> usize {
68 if (coupling == Coupling::Rear) ^ self.reversed_to_train {
69 self.train_vehicle_count - self.index_in_train - 1
70 } else {
71 self.index_in_train
72 }
73 }
74}
75
76#[cfg(feature = "ffi")]
77#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
78pub struct Bogie {
79 index: usize,
80}
81
82#[cfg(feature = "ffi")]
83impl Bogie {
84 pub fn get(index: usize) -> Result<Self, VehicleError> {
85 match unsafe { lotus_script_sys::vehicle::bogie_is_valid(index as u32) } {
86 0 => Ok(Self { index }),
87 e => Err(e.into()),
88 }
89 }
90
91 pub fn set_rail_brake_force_newton(self, value: f32) {
98 unsafe { lotus_script_sys::vehicle::set_rail_brake_force_newton(self.index as u32, value) };
99 }
100}
101
102#[cfg(feature = "ffi")]
103#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
104pub struct Axle {
105 bogie_index: usize,
106 axle_index: usize,
107}
108
109#[cfg(feature = "ffi")]
110impl Axle {
111 pub fn get(bogie_index: usize, axle_index: usize) -> Result<Self, VehicleError> {
112 match unsafe {
113 lotus_script_sys::vehicle::axle_is_valid(bogie_index as u32, axle_index as u32)
114 } {
115 0 => Ok(Self {
116 bogie_index,
117 axle_index,
118 }),
119 e => Err(e.into()),
120 }
121 }
122
123 pub fn velocity_var_name(self) -> String {
124 format!("v_Axle_mps_{}_{}", self.bogie_index, self.axle_index)
125 }
126
127 pub fn bogie(self) -> Bogie {
128 Bogie {
129 index: self.bogie_index,
130 }
131 }
132
133 pub fn axle_index(self) -> usize {
134 self.axle_index
135 }
136
137 pub fn bogie_index(self) -> usize {
138 self.bogie_index
139 }
140
141 pub fn inverse_radius(self) -> f32 {
146 let inverse_radius = unsafe {
147 lotus_script_sys::vehicle::inverse_radius(
148 self.bogie_index as u32,
149 self.axle_index as u32,
150 )
151 };
152 assert!(!inverse_radius.is_nan());
153 assert_ne!(inverse_radius, f32::NEG_INFINITY);
154 assert_ne!(inverse_radius, f32::INFINITY);
155 inverse_radius
156 }
157
158 pub fn surface_type(self) -> SurfaceType {
160 let surface_type = unsafe {
161 lotus_script_sys::vehicle::surface_type(self.bogie_index as u32, self.axle_index as u32)
162 };
163 SurfaceType::try_from(surface_type).unwrap()
164 }
165
166 pub fn rail_quality(self) -> RailQuality {
168 let quality = unsafe {
169 lotus_script_sys::vehicle::rail_quality(self.bogie_index as u32, self.axle_index as u32)
170 };
171 RailQuality::try_from(quality).unwrap()
172 }
173
174 pub fn set_traction_force_newton(self, value: f32) {
178 unsafe {
179 lotus_script_sys::vehicle::set_traction_force_newton(
180 self.bogie_index as u32,
181 self.axle_index as u32,
182 value,
183 )
184 };
185 }
186
187 pub fn set_brake_force_newton(self, value: f32) {
193 unsafe {
194 lotus_script_sys::vehicle::set_brake_force_newton(
195 self.bogie_index as u32,
196 self.axle_index as u32,
197 value,
198 )
199 };
200 }
201}
202
203#[cfg(feature = "ffi")]
204#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
205pub struct RoadAxle {
206 index: usize,
207}
208
209#[cfg(feature = "ffi")]
210impl RoadAxle {
211 pub fn get(index: usize) -> Result<Self, VehicleError> {
212 match unsafe { lotus_script_sys::vehicle::road_axle_is_valid(index as u32) } {
213 0 => Ok(Self { index }),
214 e => Err(e.into()),
215 }
216 }
217}
218
219#[cfg(feature = "ffi")]
220#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
221pub struct RoadWheel {
222 axle_index: usize,
223 wheel_index: usize,
224}
225
226#[cfg(feature = "ffi")]
227impl RoadWheel {
228 pub fn get(axle_index: usize, wheel_index: usize) -> Result<Self, VehicleError> {
229 match unsafe {
230 lotus_script_sys::vehicle::road_wheel_is_valid(axle_index as u32, wheel_index as u32)
231 } {
232 0 => Ok(Self {
233 axle_index,
234 wheel_index,
235 }),
236 e => Err(e.into()),
237 }
238 }
239
240 pub fn velocity_var_name(self) -> String {
241 format!("v_wheel_mps_{}_{}", self.axle_index, self.wheel_index)
242 }
243
244 pub fn wheel_index(self) -> usize {
245 self.wheel_index
246 }
247
248 pub fn axle_index(self) -> usize {
249 self.axle_index
250 }
251
252 pub fn set_traction_force_newton(self, value: f32) {
254 unsafe {
255 lotus_script_sys::vehicle::set_wheel_traction_force_newton(
256 self.axle_index as u32,
257 self.wheel_index as u32,
258 value,
259 )
260 };
261 }
262
263 pub fn set_brake_force_newton(self, value: f32) {
265 unsafe {
266 lotus_script_sys::vehicle::set_wheel_brake_force_newton(
267 self.axle_index as u32,
268 self.wheel_index as u32,
269 value,
270 )
271 };
272 }
273
274 pub fn set_spring_factor(self, value: f32) {
276 unsafe {
277 lotus_script_sys::vehicle::set_wheel_spring_factor(
278 self.axle_index as u32,
279 self.wheel_index as u32,
280 value,
281 )
282 };
283 }
284}
285
286#[cfg(feature = "ffi")]
287#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
288pub struct Pantograph {
289 index: usize,
290}
291
292#[cfg(feature = "ffi")]
293impl Pantograph {
294 pub fn get(index: usize) -> Result<Self, VehicleError> {
295 match unsafe { lotus_script_sys::vehicle::pantograph_is_valid(index as u32) } {
296 0 => Ok(Self { index }),
297 e => Err(e.into()),
298 }
299 }
300
301 pub fn height(self) -> f32 {
303 let height = unsafe { lotus_script_sys::vehicle::pantograph_height(self.index as u32) };
304 assert!(!height.is_nan());
305 assert_ne!(height, f32::INFINITY);
306 height
307 }
308
309 pub fn voltage(self) -> f32 {
312 let voltage = unsafe { lotus_script_sys::vehicle::pantograph_voltage(self.index as u32) };
313 assert!(!voltage.is_nan());
314 assert_ne!(voltage, f32::INFINITY);
315 voltage
316 }
317}
318
319#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)]
321#[repr(u8)]
322pub enum RailQuality {
323 Smooth = 0,
324 Rough = 1,
325 FroggySmooth = 2,
326 FroggyRough = 3,
327 FlatGroove = 4,
328 HighSpeedSmooth = 5,
329 SmoothDirt = 6,
330 RoughDirt = 7,
331 Deraileur = 8,
332}
333
334impl TryFrom<u32> for RailQuality {
335 type Error = VehicleError;
336
337 fn try_from(value: u32) -> Result<Self, Self::Error> {
338 match value {
339 0 => Ok(RailQuality::Smooth),
340 1 => Ok(RailQuality::Rough),
341 2 => Ok(RailQuality::FroggySmooth),
342 3 => Ok(RailQuality::FroggyRough),
343 4 => Ok(RailQuality::FlatGroove),
344 5 => Ok(RailQuality::HighSpeedSmooth),
345 6 => Ok(RailQuality::SmoothDirt),
346 7 => Ok(RailQuality::RoughDirt),
347 8 => Ok(RailQuality::Deraileur),
348 value => Err(value.into()),
349 }
350 }
351}
352
353#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)]
355#[repr(u8)]
356pub enum SurfaceType {
357 Gravel = 0,
358 Street = 1,
359 Grass = 2,
360}
361
362impl TryFrom<u32> for SurfaceType {
363 type Error = VehicleError;
364
365 fn try_from(value: u32) -> Result<Self, Self::Error> {
366 match value {
367 0 => Ok(SurfaceType::Gravel),
368 1 => Ok(SurfaceType::Street),
369 2 => Ok(SurfaceType::Grass),
370 value => Err(value.into()),
371 }
372 }
373}
374
375#[derive(Clone, Copy)]
376pub struct RoadSteeringSpringDamperManipulator {
377 pub stiffness_add: f32,
379 pub stiffness_mult: f32,
381 pub damping_add: f32,
383 pub damping_mult: f32,
385}
386
387impl Default for RoadSteeringSpringDamperManipulator {
388 fn default() -> Self {
389 Self {
390 stiffness_add: 0.0,
391 stiffness_mult: 1.0,
392 damping_add: 0.0,
393 damping_mult: 1.0,
394 }
395 }
396}
397
398impl RoadSteeringSpringDamperManipulator {
399 pub fn new(
400 stiffness_add: f32,
401 stiffness_mult: f32,
402 damping_add: f32,
403 damping_mult: f32,
404 ) -> Self {
405 Self {
406 stiffness_add,
407 stiffness_mult,
408 damping_add,
409 damping_mult,
410 }
411 }
412}