use crate::prelude::*; use crate::shared::attachment::{JointOf, Joints, Peer}; use crate::shared::config::part::PartConfig; use crate::ship_editor::components::{Me, PartConfigHolder, PartInShip, Selectable}; pub fn detach_plugin(app: &mut App) { app .add_systems(Update, continue_detachment); } #[derive(Component)] struct PartiallyDisconnected; /// this method begins the detachment process pub fn try_detach( ev: On>, mut q_joints: Query<&Joints>, mut q_peer: Query<&Peer>, mut q_joint_of: Query<&JointOf>, mut q_is_hearty: Query<&Me>, mut q_dragged: Query<(&Selectable, &mut Sprite, &PartConfigHolder)>, mut commands: Commands, asset_server: Res, ) { let Ok((selectable, mut sprite, part_config)) = q_dragged.get_mut(ev.entity) else { return; }; if !selectable.is_selected { return } debug!("detaching"); // now that we know the module is selected, we can detach it // TEMPORARY BEHAVIOR // upon dragging a part that will be detached, it will become dissociated from the ship. // This means it loses its PartInShip component, and disconnected its Peer's. Other connected parts might then be disconnected. // But, it's impossible to know this tick since there's a Peer connecting the parts. // So, we need to add a component to flag part to be checked in the next tick. // If these parts aren't attached anymore, remove their PartInShip and change the sprite to disconnected. // But, these parts should still be connected to each other. // hard to detach a detached module, typically (well, this might do something different later) if !is_connected_to_hearty(ev.entity, q_joints, q_peer, q_joint_of, q_is_hearty, &mut Vec::new()) { return } // here goes nothing // needs to happen first so we don't have half-detached a part let Ok(joints) = q_joints.get(ev.entity) else { warn!("part does not have a Joints? this should be impossible..."); return; }; // part no longer in ship commands.entity(ev.entity).remove::(); sprite.image = asset_server.load(part_config.0.part.sprite_disconnected.clone()); // remove all the Peer's of this part for joint in joints.iter() { commands.entity(joint).remove::(); // we also need to mark the nearby modules for detachment let Ok(peer) = q_peer.get(joint) else { continue }; let other_part = q_joint_of.get(peer.peer_joint_entity_id).expect("Peer had invalid peer_joint_entity_id").0; if is_connected_to_hearty(other_part, q_joints, q_peer, q_joint_of, q_is_hearty, &mut Vec::new()) { continue } commands.entity(other_part).insert(PartiallyDisconnected); } } /// this function will remove PartInShip and convert sprites to disconnected, but will not separate parts from each other fn continue_detachment( mut q_partially_disconnected: Query<(Entity, &PartConfigHolder, &mut Sprite), With>, mut q_joints: Query<&Joints>, mut q_peer: Query<&Peer>, mut q_joint_of: Query<&JointOf>, mut q_is_hearty: Query<&Me>, mut commands: Commands, mut asset_server: Res, ) { for (part_entity, part_config, mut sprite) in q_partially_disconnected.iter_mut() { if !is_connected_to_hearty(part_entity, q_joints, q_peer, q_joint_of, q_is_hearty, &mut Vec::new()) { return } let Ok(joints) = q_joints.get(part_entity) else { warn!("part does not have a Joints? this should be impossible..."); return; }; commands.entity(part_entity).remove::(); sprite.image = asset_server.load(part_config.0.part.sprite_disconnected.clone()); for joint in joints.iter() { // we also need to mark the nearby modules for detachment let Ok(peer) = q_peer.get(joint) else { continue }; let other_part = q_joint_of.get(peer.peer_joint_entity_id).expect("Peer had invalid peer_joint_entity_id").0; if is_connected_to_hearty(other_part, q_joints, q_peer, q_joint_of, q_is_hearty, &mut Vec::new()) { continue } commands.entity(other_part).insert(PartiallyDisconnected); } } } fn is_connected_to_hearty( part: Entity, mut q_joints: Query<&Joints>, mut q_peer: Query<&Peer>, mut q_joint_of: Query<&JointOf>, mut q_is_hearty: Query<&Me>, visited_joints: &mut Vec, ) -> bool { if let Ok(_) = q_is_hearty.get(part) { return true; } let Ok(our_joints) = q_joints.get(part).cloned() else { warn!("part does not have a Joints? this should be impossible..."); return false; }; for joint in our_joints.iter() { visited_joints.push(joint); let Ok(peer) = q_peer.get(joint) else { continue }; if visited_joints.contains(&peer.peer_joint_entity_id) { continue; } let other_part = q_joint_of.get(peer.peer_joint_entity_id).expect("Peer had invalid peer_joint_entity_id").0; // here we detect hearty in the regular codebase. that is dumb. the first thing that happens in the function is finding hearty let other_connected = is_connected_to_hearty( other_part, q_joints.reborrow(), q_peer.reborrow(), q_joint_of.reborrow(), q_is_hearty.reborrow(), visited_joints, ); if other_connected { return true; } } false }