Skip to main content

traaains/editor/train/placing/states/
placed_state.rs

1use std::fmt::Display;
2
3use bevy::{
4    asset::Assets,
5    ecs::{
6        entity::Entity,
7        query::With,
8        system::{Commands, Query, Res, ResMut, Single},
9    },
10    mesh::{Mesh, Mesh2d},
11    sprite_render::MeshMaterial2d,
12    state::state::{NextState, SubStates},
13    transform::components::Transform,
14};
15
16use crate::{
17    AssetType, AssetsManager,
18    editor::{
19        EditorState,
20        train::placing::{CurrentPlacingTrain, VirtualBogie, VirtualCarriage, VirtualTrain},
21    },
22    sim::{
23        Acceleration, Controls, Velocity,
24        track::pos::TrackPos,
25        train::{
26            Bogie, Carriage, CarriageBogies, Train, TrainMember, TrainMode,
27            on_carriage_press_window,
28        },
29    },
30    utils::Meshable,
31};
32
33use super::hovering::IsHoveringTrack;
34
35#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, SubStates)]
36#[source(IsHoveringTrack = IsHoveringTrack)]
37pub enum PlacedTrainState {
38    #[default]
39    Invalid,
40    Valid,
41}
42
43impl Display for PlacedTrainState {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        f.write_str(match self {
46            PlacedTrainState::Invalid => "Invalid",
47            PlacedTrainState::Valid => "Valid",
48        })
49    }
50}
51
52fn spawn_bogie(
53    cmd: &mut Commands,
54    q_bogies: Query<&TrackPos, With<VirtualBogie>>,
55    v_bogie: Entity,
56) -> Entity {
57    let bogie = q_bogies.get(v_bogie).unwrap();
58    cmd.entity(v_bogie).despawn();
59
60    cmd.spawn((Bogie { length: 0.0 }, *bogie, Transform::default()))
61        .id()
62}
63
64/// Copies virtual train's parameters along with the template and places an actual train
65pub fn place_train(
66    mut cmd: Commands,
67    q_train: Single<(&VirtualTrain, &TrackPos)>,
68    q_carriages: Query<&VirtualCarriage>,
69    q_bogies: Query<&TrackPos, With<VirtualBogie>>,
70    curr_placing_train: Res<CurrentPlacingTrain>,
71    assets_manager: Res<AssetsManager>,
72    mut meshes: ResMut<Assets<Mesh>>,
73    mut next_state: ResMut<NextState<EditorState>>,
74) {
75    let mut carriages = Vec::new();
76    let mut total_mass = 0.0;
77    let mut total_power = 0.0;
78
79    for (carriage_ent, carriage_template) in q_train
80        .0
81        .carriages
82        .iter()
83        .zip(curr_placing_train.carriages.iter())
84    {
85        let virtual_carriage = q_carriages.get(*carriage_ent).unwrap();
86
87        let carriage_bogies = match virtual_carriage.bogies {
88            CarriageBogies::Single(v_bogie) => {
89                CarriageBogies::Single(spawn_bogie(&mut cmd, q_bogies, v_bogie))
90            }
91            CarriageBogies::Double { front, back } => CarriageBogies::Double {
92                front: spawn_bogie(&mut cmd, q_bogies, front),
93                back: spawn_bogie(&mut cmd, q_bogies, back),
94            },
95        };
96
97        let carriage = Carriage {
98            length: carriage_template.length,
99            width: carriage_template.width,
100            mass: carriage_template.mass,
101            power: carriage_template.power,
102            bogies: carriage_bogies,
103        };
104        let spawned_carriage = cmd
105            .spawn((
106                Transform::default(),
107                Mesh2d(meshes.add(carriage.mesh(()))),
108                MeshMaterial2d(
109                    assets_manager
110                        .materials
111                        .get(&AssetType::CarriageNormal)
112                        .cloned()
113                        .unwrap(),
114                ),
115                carriage,
116            ))
117            .observe(on_carriage_press_window)
118            .id();
119
120        total_mass += carriage_template.mass;
121        total_power += carriage_template.power;
122        carriages.push(spawned_carriage);
123    }
124
125    // in a realistic model, carriages themselves would have acceleration and velocity, but since
126    // couplers aren't currently modeled, there's no way to simulate them pushing/pulling on each other
127    let spawned_train = cmd
128        .spawn((
129            Train {
130                carriages: carriages.clone(),
131                total_mass,
132                total_power,
133                mode: TrainMode::Manual,
134            },
135            Acceleration::default(),
136            Velocity::default(),
137            Controls {
138                throttle: 0.0,
139                brake: 0.0,
140            },
141        ))
142        .id();
143
144    for c in carriages {
145        cmd.entity(c).insert(TrainMember {
146            parent: spawned_train,
147        });
148    }
149
150    next_state.set(EditorState::Viewing);
151}