use crate::attachment::{Joint, JointId, JointOf, Joints, Peer, SnapOf, SnapOfJoint};
use crate::config::part::{JointConfig, PartConfig};
use crate::ecs::{Part, PartHandle};
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
pub fn part_management_plugin(app: &mut App) {
app.add_systems(PreUpdate, (handle_ready_parts, handle_part_reloading));
}
#[derive(Bundle)]
pub struct SpawnPartBundle {
pub req: SpawnPartRequest,
pub transform: Transform,
}
#[derive(Component)]
pub struct SpawnPartRequest(pub Handle<PartConfig>);
// wait for parts assets to be ready, then spawn the full part
fn handle_ready_parts(
loading_parts: Query<(Entity, &SpawnPartRequest)>,
mut commands: Commands,
assets: Res<Assets<PartConfig>>,
) {
for (entity, loading_part) in &loading_parts {
if let Some(strong_config) = assets.get(&loading_part.0) {
// config is strong; spawn 'er in!
commands
.entity(entity)
.insert(calculate_bundle(strong_config, &loading_part.0))
.remove::<SpawnPartRequest>();
spawn_joints(strong_config, entity, commands.reborrow());
}
}
}
fn handle_part_reloading(
existing_parts: Query<(Entity, &PartHandle, &Joints)>,
joints: Query<(&mut Joint, Option<&Peer>, Entity)>,
snaps: Query<(Entity, &SnapOfJoint)>,
assets: Res<Assets<PartConfig>>,
mut asset_events: MessageReader<AssetEvent<PartConfig>>,
mut commands: Commands,
) {
for event in asset_events.read() {
if let AssetEvent::Modified { id } = event {
let config = assets.get(*id).unwrap();
for existing_part in existing_parts.iter() {
if existing_part.1.0.id() == *id {
commands
.entity(existing_part.0)
.insert(calculate_bundle(config, &existing_part.1.0));
// update all joints
let mut used_joints = vec![];
for joint_id in &**existing_part.2 {
// find in config
let Ok((joint, peer, _)) = joints.get(*joint_id) else {
continue;
};
let joint_cfg = config.joints.iter().find(|u| {
joint.id == JointId::from_part_and_joint_id(&config.part.name, &u.id)
});
let Some(joint_cfg) = joint_cfg else {
if let Some(peer_id) = peer
&& let Ok(peer) = joints.get(peer_id.peer_joint_entity_id) {
commands.entity(peer.2).remove::<Peer>();
}
commands.entity(*joint_id).despawn();
for snap in &snaps {
if snap.1.0 == *joint_id {
commands.entity(snap.0).despawn();
}
}
continue;
};
used_joints.push(joint.id.clone());
commands.entity(*joint_id).insert(spawn_joint_bundle(
joint_cfg,
config,
&existing_part.0,
));
// annihilate all snaps then respawn
for snap in &snaps {
if snap.1.0 == *joint_id {
commands.entity(snap.0).despawn();
}
}
commands.spawn(spawn_snap_bundle(joint_cfg, &existing_part.0, joint_id));
}
for joint in &config.joints {
let id = JointId::from_part_and_joint_id(&config.part.name, &joint.id);
if used_joints.contains(&id) {
continue;
}
let joint_id = commands
.spawn(spawn_joint_bundle(joint, config, &existing_part.0))
.id();
commands.spawn(spawn_snap_bundle(joint, &existing_part.0, &joint_id));
}
}
}
}
}
}
fn calculate_bundle(config: &PartConfig, handle: &Handle<PartConfig>) -> impl Bundle {
let part = Part {
strong_config: config.clone(),
};
let part_handle = PartHandle(handle.clone());
let collider = Collider::rectangle(config.physics.width, config.physics.height);
let mass = Mass(config.physics.mass);
(
part,
part_handle,
collider,
mass,
)
}
fn spawn_joint_bundle(joint: &JointConfig, part: &PartConfig, parent: &Entity) -> impl Bundle {
let j_comp = Joint {
id: JointId::from_part_and_joint_id(part.part.name.clone(), joint.id.clone()),
transform: joint.target.into(),
};
let joint_transform: Transform = j_comp.transform;
let joint_of = JointOf(*parent);
(j_comp, joint_transform, joint_of, Replicated)
}
fn spawn_snap_bundle(joint: &JointConfig, parent: &Entity, p_joint: &Entity) -> impl Bundle {
let snap_transform: Transform = joint.snap.into();
let snap_for = SnapOf(*parent);
let snap_of = SnapOfJoint(*p_joint);
(snap_transform, snap_for, snap_of, Replicated)
}
fn spawn_joints(config: &PartConfig, parent: Entity, mut commands: Commands) {
for joint in &config.joints {
let joint_id = commands
.spawn(spawn_joint_bundle(joint, config, &parent))
.id();
commands.spawn(spawn_snap_bundle(joint, &parent, &joint_id));
}
}