~starkingdoms/starkingdoms

ref: 1f4fec66a1e4c2148cc8ef59bf7adfb1cb77e3df starkingdoms/crates/unified/src/ship_editor/detach.rs -rw-r--r-- 5.5 KiB
1f4fec66ghostly_zsh ship editor feat: detaching works ish 2 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
}