Skip to main content

traaains/editor/track/placing/states/
current_step.rs

1use std::fmt::Display;
2
3use bevy::{
4    asset::Assets,
5    ecs::{
6        entity::Entity,
7        observer::ObserverSystemExt,
8        query::With,
9        system::{Commands, Query, Res, ResMut, Single},
10    },
11    math::Vec2,
12    mesh::{Mesh, Mesh2d},
13    sprite_render::MeshMaterial2d,
14    state::{condition::in_state, state::SubStates},
15    transform::components::Transform,
16};
17use bezier::{Cubic, Linear, Quadratic};
18
19use crate::{
20    AssetType, AssetsManager,
21    editor::{
22        EditorState,
23        track::{
24            TrackEditorState,
25            placing::{
26                FromNodeType, LastConnectedBlock, NodeType, Placing, VirtualSegment,
27                take_points,
28            },
29        },
30    },
31    game::cursor::CursorPos,
32    shape::{BezierMeshProps, Shapes},
33    sim::track::node::{TrackNode, on_node_press_window},
34    utils::Meshable,
35};
36
37#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, SubStates)]
38#[source(TrackEditorState = TrackEditorState::Placing)]
39pub enum CurrentStep {
40    #[default]
41    Zero,
42    Linear,
43    Quadratic,
44    Cubic,
45}
46
47impl Display for CurrentStep {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        f.write_str(match self {
50            CurrentStep::Zero => "-",
51            CurrentStep::Linear => "Linear",
52            CurrentStep::Quadratic => "Quadratic",
53            CurrentStep::Cubic => "Cubic",
54        })
55    }
56}
57
58/// "Unfinished nodes" are nodes (or more precisely the node) that were placed as fresh nodes for the current virtual segment
59/// and need to be despawned when removing the first step.
60pub fn on_enter_constant(
61    mut cmd: Commands,
62    v_segment: Single<Entity, With<VirtualSegment>>,
63    unfinished_node: Option<Single<Entity, (With<TrackNode>, With<Placing>)>>,
64    mut last_connected_block: ResMut<LastConnectedBlock>,
65) {
66    cmd.entity(*v_segment).despawn();
67
68    if let Some(unfinished_node) = unfinished_node {
69        cmd.entity(*unfinished_node).despawn();
70    }
71    // we reset the "chain" of placed segments and thus probably no longer want to continue placing the same block
72    last_connected_block.0 = None;
73}
74
75/// System that reacts to exits from constant step and spawns the virtual segment
76pub fn on_exit_constant(
77    mut cmd: Commands,
78    q_nodes: Query<&Transform, With<TrackNode>>,
79    cursor_pos: Res<CursorPos>,
80    assets_manager: Res<AssetsManager>,
81    mut from_node_type: ResMut<FromNodeType>,
82    mut meshes: ResMut<Assets<Mesh>>,
83) {
84    let (from_node_id, virtual_segment_transform) = match *from_node_type {
85        FromNodeType(NodeType::Virtual) => (
86            cmd.spawn((
87                TrackNode::default(),
88                Placing,
89                Transform::from_translation(cursor_pos.0.extend(5.0)),
90                Mesh2d(
91                    assets_manager
92                        .meshes
93                        .get(&AssetType::TrackNodeNormal)
94                        .cloned()
95                        .unwrap(),
96                ),
97                MeshMaterial2d(
98                    assets_manager
99                        .materials
100                        .get(&AssetType::TrackNodeNormal)
101                        .cloned()
102                        .unwrap(),
103                ),
104            ))
105            .observe(on_node_press_window.run_if(in_state(EditorState::Viewing)))
106            .id(),
107            Transform::from_translation(cursor_pos.0.extend(0.0)),
108        ),
109        FromNodeType(NodeType::Physical(e)) => {
110            let mut transform = *q_nodes.get(e).unwrap();
111            transform.translation.z = 0.0;
112            (e, transform)
113        }
114    };
115
116    let shape = Linear::new([Vec2::ZERO, Vec2::new(1.0, 1.0)]);
117    let mesh = shape.mesh(BezierMeshProps::default());
118
119    cmd.spawn((
120        VirtualSegment {
121            shape: Shapes::Linear(shape),
122            from: from_node_id,
123            to: NodeType::Virtual,
124        },
125        Placing,
126        virtual_segment_transform,
127        Mesh2d(meshes.add(mesh)),
128        MeshMaterial2d(
129            assets_manager
130                .materials
131                .get(&AssetType::TrackSegmentGhost)
132                .cloned()
133                .unwrap(),
134        ),
135    ));
136
137    *from_node_type = FromNodeType(NodeType::Virtual);
138}
139
140pub fn on_linear_to_quadratic(
141    mut v_segment: Single<(&mut VirtualSegment, &mut Mesh2d), With<VirtualSegment>>,
142    mut meshes: ResMut<Assets<Mesh>>,
143) {
144    let [a, b] = take_points(&v_segment.0.shape);
145    let shape = Quadratic::new([a, b, b]);
146    v_segment.1.0 = meshes.add(shape.mesh(BezierMeshProps::default()));
147    v_segment.0.shape = Shapes::Quadratic(shape);
148    v_segment.0.to = NodeType::Virtual;
149}
150
151pub fn on_quadratic_to_linear(
152    mut v_segment: Single<(&mut VirtualSegment, &mut Mesh2d), With<VirtualSegment>>,
153    mut meshes: ResMut<Assets<Mesh>>,
154) {
155    let [a, b] = take_points(&v_segment.0.shape);
156    let shape = Linear::new([a, b]);
157    v_segment.1.0 = meshes.add(shape.mesh(BezierMeshProps::default()));
158    v_segment.0.shape = Shapes::Linear(shape);
159    v_segment.0.to = NodeType::Virtual;
160}
161
162pub fn on_quadratic_to_cubic(
163    mut v_segment: Single<(&mut VirtualSegment, &mut Mesh2d), With<VirtualSegment>>,
164    mut meshes: ResMut<Assets<Mesh>>,
165) {
166    let [a, b, c] = take_points(&v_segment.0.shape);
167    let shape = Cubic::new([a, b, c, c]);
168    v_segment.1.0 = meshes.add(shape.mesh(BezierMeshProps::default()));
169    v_segment.0.shape = Shapes::Cubic(shape);
170    v_segment.0.to = NodeType::Virtual;
171}
172
173pub fn on_cubic_to_quadratic(
174    mut v_segment: Single<(&mut VirtualSegment, &mut Mesh2d), With<VirtualSegment>>,
175    mut meshes: ResMut<Assets<Mesh>>,
176) {
177    let [a, b, c] = take_points(&v_segment.0.shape);
178    let shape = Quadratic::new([a, b, c]);
179    v_segment.1.0 = meshes.add(shape.mesh(BezierMeshProps::default()));
180    v_segment.0.shape = Shapes::Quadratic(shape);
181    v_segment.0.to = NodeType::Virtual;
182}
183
184pub fn on_cubic_to_linear(
185    mut v_segment: Single<(&mut VirtualSegment, &mut Mesh2d), With<VirtualSegment>>,
186    mut meshes: ResMut<Assets<Mesh>>,
187) {
188    let [a, b] = take_points(&v_segment.0.shape);
189    let shape = Linear::new([a, b]);
190    v_segment.1.0 = meshes.add(shape.mesh(BezierMeshProps::default()));
191    v_segment.0.shape = Shapes::Linear(shape);
192    v_segment.0.to = NodeType::Virtual;
193}