use bevy::asset::Handle;
use bevy::prelude::*;
use bevy_rapier2d::prelude::{AdditionalMassProperties, Collider};
use bevy_replicon::prelude::Replicated;
use crate::attachment::{Joint, JointId, JointOf, JointSnapFor, JointSnaps, Joints};
use crate::config::part::{JointOffset, PartConfig};
use crate::ecs::Part;
pub fn part_config_plugin(app: &mut App) {
app.add_systems(PreUpdate, handle_spawn_part_requests)
.add_systems(Update, update_part_requests);
}
#[derive(Component, Debug)]
pub struct SpawnPart(pub String);
#[derive(Component)]
struct LoadingPart(Handle<PartConfig>);
#[derive(Component, Debug)]
struct PartType(AssetId<PartConfig>);
#[derive(Component)]
/// STOP DELETING MY ASSET BEVY
struct LiveConfigHandle(Handle<PartConfig>);
// watch for new SpawnPart components and start loading their config files
fn handle_spawn_part_requests(new_parts: Query<(Entity, &SpawnPart), Added<SpawnPart>>, mut commands: Commands, asset_server: Res<AssetServer>, assets: Res<Assets<PartConfig>>, parts: Query<(&Joints, &JointSnaps), With<Part>>,) {
for (new_part, request) in &new_parts {
trace!(?new_part, ?request, "answering part request");
let hdl: Handle<PartConfig> = asset_server.load(request.0.clone());
commands.entity(new_part)
.remove::<SpawnPart>()
.insert(LiveConfigHandle(hdl.clone()));
if let Some(cfg) = assets.get(&hdl) {
spawn_part(commands.reborrow(), new_part, cfg, &hdl.id(), parts, false);
} else {
commands.entity(new_part)
.insert(LoadingPart(hdl.clone()));
}
}
}
fn update_part_requests(
mut ev_config: EventReader<AssetEvent<PartConfig>>,
loading_parts: Query<(Entity, &LoadingPart)>,
existing_parts: Query<(Entity, &PartType)>,
mut assets: ResMut<Assets<PartConfig>>,
mut commands: Commands,
parts: Query<(&Joints, &JointSnaps), With<Part>>,
) {
for ev in ev_config.read() {
match ev {
AssetEvent::Added { id } => {
trace!(?id, "asset added");
for (loading_part, req) in &loading_parts {
if req.0.id() == *id {
let Some(asset) = assets.get(*id) else { continue; };
spawn_part(commands.reborrow(), loading_part, asset, id, parts, false);
}
}
},
AssetEvent::Modified { id } => {
trace!(?id, "updating part");
for (existing_part, ptype) in &existing_parts {
if ptype.0 == *id {
let Some(asset) = assets.get(ptype.0) else { continue; };
spawn_part(commands.reborrow(), existing_part, asset, id, parts, true);
}
}
}
_ => {}
}
}
}
fn spawn_part(mut commands: Commands, entity: Entity, part: &PartConfig, id: &AssetId<PartConfig>, parts: Query<(&Joints, &JointSnaps), With<Part>>, is_update: bool) {
commands.entity(entity)
.remove::<LoadingPart>()
.insert(Part {
sprite: part.part.sprite_disconnected.clone(),
width: part.physics.width,
height: part.physics.height,
mass: part.physics.mass,
})
.insert(Collider::cuboid(part.physics.width / 2.0, part.physics.height / 2.0))
.insert(AdditionalMassProperties::Mass(part.physics.mass))
.insert(PartType(*id))
.insert(Replicated);
for joint in &part.joints {
if is_update {
// find all entities
for (joints, snaps) in &parts {
for joint_entity in joints.iter() {
commands.entity(joint_entity).insert((
ChildOf(entity),
Joint {
id: JointId::from_part_and_joint_id(part.part.name.clone(), joint.id.clone()),
transform: joint.target.into()
},
<JointOffset as Into<Transform>>::into(joint.target),
JointOf(entity),
Replicated,
));
}
for snap_entity in snaps.iter() {
commands.entity(snap_entity).insert((
<JointOffset as Into<Transform>>::into(joint.snap),
));
}
}
} else {
let e = commands.spawn((
ChildOf(entity),
Joint {
id: JointId::from_part_and_joint_id(part.part.name.clone(), joint.id.clone()),
transform: joint.target.into()
},
<JointOffset as Into<Transform>>::into(joint.target),
JointOf(entity),
Replicated,
)).id();
commands.spawn((
ChildOf(entity),
JointSnapFor(e),
<JointOffset as Into<Transform>>::into(joint.snap),
Replicated,
));
}
}
}