traaains/editor/track/placing/states/
final_step.rs1use bevy::{
2 asset::Assets,
3 ecs::{
4 query::{With, Without},
5 system::{Commands, Query, Res, ResMut, Single},
6 },
7 math::Vec2,
8 mesh::{Mesh, Mesh2d},
9 sprite_render::{ColorMaterial, MeshMaterial2d},
10 state::state::{ComputedStates, NextState},
11 transform::components::Transform,
12};
13use bezier::Linear;
14
15use crate::{
16 AssetType, AssetsManager,
17 editor::track::placing::{
18 LastConnectedBlock, NodeType, Placing, VirtualSegment,
19 states::{current_shape::CurrentShape, current_step::CurrentStep},
20 },
21 game::cursor::CursorPos,
22 shape::{BezierMeshProps, Shapes},
23 sim::{
24 logical_graph::LogicalBlock,
25 track::{
26 node::TrackNode,
27 segment::{TrackSegment, commands::SpawnSegment, signals_manager::SignalsManager},
28 },
29 },
30 utils::Meshable,
31};
32
33#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
34pub struct FinalStep;
35
36impl ComputedStates for FinalStep {
37 type SourceStates = (CurrentShape, CurrentStep);
38
39 fn compute(sources: Self::SourceStates) -> Option<Self> {
40 match sources {
41 (CurrentShape::Linear, CurrentStep::Linear) => Some(FinalStep),
42 (CurrentShape::Quadratic, CurrentStep::Quadratic) => Some(FinalStep),
43 (CurrentShape::Cubic, CurrentStep::Cubic) => Some(FinalStep),
44 _ => None,
45 }
46 }
47}
48
49pub fn on_enter_final_step(
50 mut v_segment: Single<&mut MeshMaterial2d<ColorMaterial>, With<VirtualSegment>>,
51 assets_manager: Res<AssetsManager>,
52) {
53 v_segment.0 = assets_manager
54 .materials
55 .get(&AssetType::TrackSegmentNormal)
56 .cloned()
57 .unwrap();
58}
59
60pub fn on_exit_final_step(
61 mut v_segment: Single<&mut MeshMaterial2d<ColorMaterial>, With<VirtualSegment>>,
62 assets_manager: Res<AssetsManager>,
63) {
64 v_segment.0 = assets_manager
65 .materials
66 .get(&AssetType::TrackSegmentGhost)
67 .cloned()
68 .unwrap();
69}
70
71pub fn place_segment(
75 mut cmd: Commands,
76 mut v_segment: Single<(&mut VirtualSegment, &mut Transform, &mut Mesh2d)>,
77 q_nodes: Query<(&mut TrackNode, &Transform), Without<VirtualSegment>>,
78 q_segments: Query<&TrackSegment>,
79 cursor_pos: Res<CursorPos>,
80 mut last_connected_block: ResMut<LastConnectedBlock>,
81 mut next_step: ResMut<NextState<CurrentStep>>,
82 mut meshes: ResMut<Assets<Mesh>>,
83 assets_manager: Res<AssetsManager>,
84) {
85 let (from_node, _) = q_nodes.get(v_segment.0.from).unwrap();
87 if !from_node.connections.has_free() {
88 return;
89 }
90
91 let (node_to, final_world_pos, node_to_lb) = match v_segment.0.to {
93 NodeType::Physical(entity) => {
94 let (to_node, to_node_transform) = q_nodes.get(entity).unwrap();
95
96 if !to_node.connections.has_free() {
98 return;
99 }
100
101 let lb = to_node
103 .connections
104 .iter()
105 .next()
106 .map(|c| q_segments.get(c.segment).map(|s| s.block).unwrap());
107
108 (entity, to_node_transform.translation.truncate(), lb)
109 }
110 NodeType::Virtual => {
111 let new_node = TrackNode::spawn(
112 &mut cmd,
113 Transform::from_translation(cursor_pos.0.extend(5.0)),
114 assets_manager
115 .meshes
116 .get(&AssetType::TrackNodeNormal)
117 .cloned()
118 .unwrap(),
119 assets_manager
120 .materials
121 .get(&AssetType::TrackNodeNormal)
122 .cloned()
123 .unwrap(),
124 );
125
126 (new_node, cursor_pos.0, None)
127 }
128 };
129
130 cmd.entity(v_segment.0.from).remove::<Placing>();
132
133 let new_logical_block = last_connected_block.0.unwrap_or_else(|| {
135 let new_block = from_node
136 .connections
137 .iter()
138 .next()
139 .map(|c| q_segments.get(c.segment).map(|s| s.block).unwrap())
140 .unwrap_or_else(|| {
141 node_to_lb.unwrap_or_else(|| cmd.spawn(LogicalBlock::default()).id())
142 });
143
144 last_connected_block.0 = Some(new_block);
145 new_block
146 });
147
148 let final_segment_pos = final_world_pos - v_segment.1.translation.truncate();
151 v_segment.0.shape.set_last_point(final_segment_pos);
152 let cloned_shape = v_segment.0.shape.clone();
153
154 cmd.queue(SpawnSegment::new(
155 TrackSegment::new(cloned_shape, v_segment.0.from, node_to, new_logical_block),
156 SignalsManager::default(),
157 *v_segment.1,
158 ));
159
160 let reset_shape = Linear::new([Vec2::ZERO, Vec2::from((1.0, 1.0))]);
162 v_segment.2.0 = meshes.add(reset_shape.mesh(BezierMeshProps::default()));
163 v_segment.0.shape = Shapes::Linear(reset_shape);
164 v_segment.1.translation = final_world_pos.extend(0.0);
165 v_segment.0.from = node_to;
166 v_segment.0.to = NodeType::Virtual;
167
168 next_step.set(CurrentStep::Linear);
170}