Skip to main content

traaains/editor/track/placing/
observers.rs

1use bevy::{
2    asset::Assets,
3    ecs::{
4        component::Component,
5        event::EntityEvent,
6        observer::On,
7        query::{With, Without},
8        system::{Query, Res, ResMut, Single},
9    },
10    mesh::{Mesh, Mesh2d},
11    picking::events::{PointerEnter, PointerLeave},
12    sprite_render::{ColorMaterial, MeshMaterial2d},
13    transform::components::Transform,
14};
15
16use crate::{
17    AssetsManager,
18    editor::track::placing::{FromNodeType, NodeType, VirtualNode, VirtualSegment},
19    shape::BezierMeshProps,
20    sim::track::node::TrackNode,
21    utils::Meshable,
22};
23
24/// Marker component for the observers in this editor.
25#[derive(Component)]
26pub struct PlacingTrackObserver;
27
28/// Observer responding to the user entering their pointer to a node and responding accordingly, changing either the from or to nodes to physical ones.
29pub fn on_enter_node(
30    e: On<PointerEnter>,
31    mut virtual_s: Option<
32        Single<(&mut VirtualSegment, &Transform, &mut Mesh2d), Without<VirtualNode>>,
33    >,
34    mut virtual_n: Single<&mut Transform, With<VirtualNode>>,
35    mut q_nodes: Query<
36        (&Transform, &mut MeshMaterial2d<ColorMaterial>),
37        (With<TrackNode>, Without<VirtualNode>),
38    >,
39    mut from_node_type: ResMut<FromNodeType>,
40    mut meshes: ResMut<Assets<Mesh>>,
41    assets_manager: Res<AssetsManager>,
42) {
43    // only target nodes
44    let Ok((tn_transform, mut tn_mat)) = q_nodes.get_mut(e.event_target()) else {
45        return;
46    };
47
48    // if there is no virtual segment, then change from node type to physical since we want to build from this node
49    match virtual_s.as_mut() {
50        Some(virtual_s) => {
51            // ignore self reference
52            if virtual_s.0.from == e.event_target() {
53                return;
54            }
55
56            let last_point_pos = (tn_transform.translation - virtual_s.1.translation).truncate();
57
58            virtual_s.0.shape.set_last_point(last_point_pos);
59            virtual_s.0.to = NodeType::Physical(e.event_target());
60            virtual_s.2.0 = meshes.add(virtual_s.0.shape.mesh(BezierMeshProps::default()));
61        }
62        None => {
63            from_node_type.0 = NodeType::Physical(e.event_target());
64            tn_mat.0 = assets_manager
65                .materials
66                .get(&crate::AssetType::TrackNodeActive)
67                .cloned()
68                .unwrap();
69            virtual_n.translation = tn_transform.translation;
70        }
71    }
72}
73
74pub fn on_exit_node(
75    e: On<PointerLeave>,
76    virtual_s: Option<Single<&mut VirtualSegment>>,
77    mut q_nodes: Query<&mut MeshMaterial2d<ColorMaterial>, With<TrackNode>>,
78    mut from_node_type: ResMut<FromNodeType>,
79    assets_manager: Res<AssetsManager>,
80) {
81    // only target nodes
82    let Ok(mut tn_mat) = q_nodes.get_mut(e.event_target()) else {
83        return;
84    };
85
86    match virtual_s {
87        // if there is a virtual segment, we want to switch its "to" type to virtual
88        Some(mut virtual_s) => {
89            virtual_s.to = NodeType::Virtual;
90        }
91        // if there is no virtual segment, we want to switch back to placing from a virtual node
92        None => {
93            if let NodeType::Physical(_) = from_node_type.0 {
94                from_node_type.0 = NodeType::Virtual;
95            }
96        }
97    }
98
99    tn_mat.0 = assets_manager
100        .materials
101        .get(&crate::AssetType::TrackNodeNormal)
102        .cloned()
103        .unwrap();
104}