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<Pointer<DragStart>>,
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<AssetServer>,
) {
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::<PartInShip>();
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::<Peer>();
// 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<PartiallyDisconnected>>,
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<AssetServer>,
) {
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::<PartInShip>();
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<Entity>,
) -> 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
}