use bevy::color::palettes::css::{ORANGE, RED}; use crate::client::Me; use crate::ecs::{CursorWorldCoordinates, DragRequestEvent, Part}; use bevy::prelude::*; use bevy_rapier2d::dynamics::MassProperties; use bevy_rapier2d::prelude::AdditionalMassProperties; use crate::attachment::{JointOf, PartInShip, SnapOf, SnapOfJoint}; use crate::client::colors::GREEN; use crate::client::key_input::AttachmentDebugRes; pub fn parts_plugin(app: &mut App) { app.insert_resource(DragResource(None)); app.add_systems(Update, (handle_incoming_parts, handle_updated_parts, update_drag_ghosts)); app.add_observer(on_part_release); } fn handle_incoming_parts( mut commands: Commands, new_parts: Query<(Entity, &Part), Added>, asset_server: Res, ) { for (new_entity, new_part) in new_parts.iter() { let mut sprite = Sprite::from_image(asset_server.load(&new_part.strong_config.part.sprite_disconnected)); sprite.custom_size = Some(Vec2::new(new_part.strong_config.physics.width, new_part.strong_config.physics.height)); commands .entity(new_entity) .insert(sprite) .insert(AdditionalMassProperties::MassProperties(MassProperties { local_center_of_mass: Vec2::ZERO, mass: new_part.strong_config.physics.mass, principal_inertia: 7.5, })) .insert(Pickable::default()) .observe(on_part_click); } } fn handle_updated_parts( mut commands: Commands, updated_parts: Query<(Entity, &Part), Changed>, asset_server: Res, ) { for (updated_entity, updated_part) in updated_parts.iter() { let mut sprite = Sprite::from_image(asset_server.load(&updated_part.strong_config.part.sprite_disconnected)); sprite.custom_size = Some(Vec2::new(updated_part.strong_config.physics.width, updated_part.strong_config.physics.height)); commands .entity(updated_entity) .remove::() .remove::() .insert(sprite) .insert(AdditionalMassProperties::MassProperties(MassProperties { local_center_of_mass: Vec2::ZERO, mass: updated_part.strong_config.physics.mass, principal_inertia: 7.5, })); } } #[derive(Resource)] struct DragResource(Option); #[derive(Component)] struct DragGhost; fn on_part_click( ev: Trigger>, sprites: Query<(&Sprite, &Transform), Without>, mut drag: ResMut, mut commands: Commands ) { if ev.button != PointerButton::Primary { return; } let Ok(sprite) = sprites.get(ev.target()) else { return; }; let mut s = sprite.0.clone(); s.color = Color::srgba(0.7, 0.7, 0.7, 1.0); commands.spawn(( DragGhost, sprite.1.clone(), s )); drag.0 = Some(ev.target()); } fn on_part_release( ev: Trigger>, mut drag: ResMut, mut events: EventWriter, cursor: Res, mut commands: Commands, ghosts: Query>, ) { if ev.button != PointerButton::Primary { return; } if let Some(e) = drag.0 { for ghost in &ghosts { commands.entity(ghost).despawn(); } if let Some(c) = cursor.0 { debug!(?e, ?c, "sending drag request"); events.write(DragRequestEvent(e, c)); } } drag.0 = None; } fn update_drag_ghosts( mut ghost: Single<&mut Transform, (With, Without, Without, Without)>, cursor: Res, snaps: Query<(&Transform, &SnapOfJoint, &SnapOf)>, joints: Query<(&Transform, &JointOf)>, parts: Query<&GlobalTransform, With>, debug: Res, mut gizmos: Gizmos, ) { let Some(cursor) = cursor.0 else { return }; const CUTOFF: f32 = 25.0; // px let mut best_distance = f32::INFINITY; let mut best_target = Transform::from_xyz(cursor.x, cursor.y, 0.0); for (snap_local_transform, snap_joint, snap_part) in &snaps { let Ok(parent_position) = parts.get(snap_part.0) else { continue; }; let snap_global_translation = parent_position.transform_point(snap_local_transform.translation).xy(); let distance_to_cursor = cursor.distance(snap_global_translation); if distance_to_cursor > best_distance { if debug.0 {gizmos.circle_2d(snap_global_translation, 3.0, RED); } continue; } if distance_to_cursor > CUTOFF { if debug.0 {gizmos.circle_2d(snap_global_translation, 3.0, ORANGE); } continue; } if debug.0 { gizmos.circle_2d(snap_global_translation, 3.0, GREEN); } let Ok((offset, parent)) = joints.get(snap_joint.0) else { continue; }; let Ok(parent_pos) = parts.get(parent.0) else { continue; }; let joint_target = parent_pos.transform_point(offset.translation); if debug.0 { gizmos.circle_2d(joint_target.xy(), 3.0, GREEN); } let target_transform = Transform { translation: joint_target, rotation: parent_position.rotation().mul_quat(offset.rotation), scale: offset.scale, }; best_distance = distance_to_cursor; best_target = target_transform; } **ghost = best_target; }