traaains/sim/train/mod.rs
1//! Trains.
2//!
3//! Trains are position-less entities, simply storing a vector of references to their carriages (and, currently, an acceleration and velocity).
4//!
5//! Carriages are also track position-less entities. Each has a certain length, width and mass. They have a system of bogies,
6//! to which they position themselves.
7//!
8//! Bogies have both a track position and a world transform. On each frame, after bogie world transforms are updated based on their track
9//! positions, carriages are aligned respective to their bogies.
10
11use bevy::{
12 ecs::{
13 component::Component,
14 entity::Entity,
15 event::EntityEvent,
16 observer::On,
17 query::With,
18 system::{Query, ResMut},
19 },
20 picking::events::PointerPress,
21 shape::Rectangle,
22 state::state::NextState,
23};
24
25use crate::{sim::track::STANDARD_GAUGE, ui::UiState, utils::Meshable};
26
27pub mod commands;
28pub mod systems;
29
30/// Where should the single bogie be mounted. This should become a modifiable property in the future.
31pub const SINGLE_BOGIE_IDX: f32 = 0.5;
32/// Where should the double bogie be mounted. This should become a modifiable property in the future.
33pub const DOUBLE_BOGIE_IDX: (f32, f32) = (0.1, 0.9);
34/// Default coupler length. This should become a modifiable property in the future.
35pub const COUPLER_LENGTH: f32 = 0.1;
36
37// TODO: This should be different per train
38pub const BRAKING_COEFF: f32 = 0.2;
39
40/// Defines the behaviour of the related train. Unfinished.
41#[derive(Clone, Default)]
42pub enum TrainMode {
43 /// The train won't do anything and will wait for the player's control.
44 /// Driver components won't do anything.
45 #[default]
46 Manual,
47 /// The train will pathfind and try to reach the node specified by the player.
48 /// Will respond to signals and change switches accordingly.
49 SemiAutomatic,
50 /// The train will go on a pre-defined route, assuming it is present (like a train schedule, for example).
51 Automatic,
52}
53
54#[derive(Clone, Component, Default)]
55pub struct Train {
56 pub carriages: Vec<Entity>,
57 /// in kilograms
58 pub total_mass: f32,
59 // in watts
60 pub total_power: f32,
61 // unimplemented
62 pub mode: TrainMode,
63}
64
65/// Component of a carriage, pointing to its parent train.
66#[derive(Component)]
67pub struct TrainMember {
68 pub parent: Entity,
69}
70
71// #[derive(Clone)]
72// /// Describes a bogie mount at a carriage
73// pub struct BogieMount {
74// /// The target bogie
75// pub bogie: Entity,
76// /// Index of the location of the mount, front to back (0.0 means front).
77// pub idx: f32,
78// }
79
80#[derive(Clone)]
81/// Describes which shape a carriage's bogies can take.
82/// For simplicity sake, it is assumed that bogies are mounted at the same location on each carriage.
83pub enum CarriageBogies {
84 Single(Entity),
85 Double { front: Entity, back: Entity },
86}
87
88/// A single carriage in a train, defined by its length, width and mass.
89#[derive(Clone, Component)]
90pub struct Carriage {
91 /// in meters
92 pub length: f32,
93 /// in meters
94 pub width: f32,
95 /// in kilograms
96 pub mass: f32,
97 // in watts
98 pub power: f32,
99 pub bogies: CarriageBogies,
100}
101
102impl Meshable for Carriage {
103 type Props = ();
104
105 fn mesh(&self, _: Self::Props) -> bevy::mesh::Mesh {
106 Rectangle::new(self.length, self.width).into()
107 }
108}
109
110/// A [bogie](https://en.wikipedia.org/wiki/Bogie).
111///
112/// Simply an entity with a track position and a transform, to which a carriage refers to for its tranform updates.
113/// For simplicity sake, it is assumed that the same force is applied to each bogie of a carriage, no matter its
114/// orientation with respect to the force. If one would like to simulate forces at the bogie level one would have
115/// to figure out how to model the relationship between the bogies and the carriage itself, since at that point each
116/// of the bogies might move a different amount and thus the simple model of drawing a line through the center of each
117/// of the bogies and aligning the carriage with it no longer works.
118#[derive(Component)]
119pub struct Bogie {
120 /// in meters
121 pub length: f32,
122}
123
124impl Meshable for Bogie {
125 type Props = ();
126
127 fn mesh(&self, _: Self::Props) -> bevy::mesh::Mesh {
128 Rectangle::new(self.length, STANDARD_GAUGE).into()
129 }
130}
131
132/// Observer that runs on left click on carriages and opens the train window
133pub fn on_carriage_press_window(
134 e: On<PointerPress>,
135 q_carriages: Query<&TrainMember, With<Carriage>>,
136 mut next_state: ResMut<NextState<UiState>>,
137) {
138 if let Ok(carriage) = q_carriages.get(e.event_target()) {
139 next_state.set(UiState::TrainWindow {
140 target: carriage.parent,
141 });
142 }
143}