use crate::{config::planet::{Planet, PlanetBundle, PlanetConfigCollection}, ecs::PlanetSensor, world_config::WorldConfigResource}; use bevy::{asset::Handle, math::DVec3}; use crate::prelude::*; use bevy_replicon::prelude::Replicated; use crate::config::planet::{PlanetSpring, PlanetSpringJoint}; pub fn planets_plugin(app: &mut App) { app.init_resource::() .add_systems(Startup, start_loading_planets) .add_systems(Update, update_planets); } #[derive(Resource, Default)] pub struct PlanetConfigResource { handle: Option>, } fn start_loading_planets(assets: Res, mut planets: ResMut) { planets.handle = Some(assets.load("config/planets.pc.toml")); } pub fn update_planets( mut commands: Commands, mut ev_config: MessageReader>, assets: ResMut>, planets: ResMut, mut q_planets: Query<( Entity, &mut Planet, &mut Transform, &mut Mass, )>, mut planet_joint_springs: Query<(&PlanetSpringJoint, &mut FixedJoint)>, world_config: Res, ) { let Some(ref world_config) = world_config.config else { return; }; let Some(handle) = planets.handle.as_ref() else { return; }; let waiting_for_asset_id = handle.id(); for ev in ev_config.read() { match ev { AssetEvent::Added { id } => { if *id == waiting_for_asset_id { debug!("planet config loaded - creating planets"); let planet_config = assets.get(*id).unwrap(); for planet in &planet_config.planets { let planet_position = vec3(planet.default_transform[0], planet.default_transform[1], planet.default_transform[2]); let mut planet_entity = commands .spawn((PlanetBundle { planet: planet.clone(), transform: Transform::from_xyz( planet.default_transform[0], planet.default_transform[1], planet.default_transform[2], ), collider: Collider::circle(planet.radius), mass: Mass(planet.mass) }, SleepingDisabled )); planet_entity.with_child(( Collider::circle(planet.radius+2.0), Sensor, PlanetSensor(planet.name.clone()), CollisionEventsEnabled, )); let planet_entity_id = planet_entity.id(); if let Some(orbit) = &planet.orbit { let Some(parent_planet) = planet_config.planets.iter().find(|u| orbit.orbiting == u.name) else { continue }; let parent_planet_position = vec3(parent_planet.default_transform[0], parent_planet.default_transform[1], parent_planet.default_transform[2]); let r = (planet_position - parent_planet_position).as_dvec3(); let g = world_config.world.gravity * parent_planet.mass as f64 / r.length_squared(); // tangential velocity let v = (g*r.length() as f64).sqrt(); let v_dir = DVec3::Z.cross(r).truncate().normalize(); let v = v_dir * v; planet_entity.insert(LinearVelocity(v)); let spring = commands.spawn(( PlanetSpring { name: planet.name.clone() }, Transform::from_xyz( planet.default_transform[0], planet.default_transform[1], planet.default_transform[2], ), RigidBody::Kinematic, )).id(); commands.spawn(( PlanetSpringJoint { name: planet.name.clone() }, FixedJoint::new(planet_entity_id, spring).with_point_compliance(planet_config.orbit.planet_spring_compliance), JointDamping { linear: planet_config.orbit.planet_spring_damping, angular: 1.0, }, )); } trace!(?planet, "new planet spawned"); } } } AssetEvent::Modified { id } => { if *id == waiting_for_asset_id { trace!("planet config modified - reloading planets"); let planet_config = assets.get(*id).unwrap(); info!("updating compliance on all planet spring joints"); planet_joint_springs.iter_mut().for_each(|mut u| u.1.point_compliance = planet_config.orbit.planet_spring_compliance); for planet in &planet_config.planets { let existing_planet = q_planets .iter_mut() .find(|(_, p, _, _)| p.name == planet.name); if let Some((existing, mut e_planet, mut e_transform, mut e_mass)) = existing_planet { commands .entity(existing) .remove::() .insert(Collider::circle(planet.radius)); *e_planet = planet.clone(); *e_mass = Mass(planet.mass); trace!(?planet, "planet hot-reloaded"); } else { let planet_entity = commands .spawn(PlanetBundle { planet: planet.clone(), transform: Transform::from_xyz( planet.default_transform[0], planet.default_transform[1], planet.default_transform[2], ), collider: Collider::circle(planet.radius), mass: Mass( planet.mass, ), }).id(); if planet.orbit.is_some() { let spring =commands.spawn(( PlanetSpring { name: planet.name.clone() }, Transform::from_xyz( planet.default_transform[0], planet.default_transform[1], planet.default_transform[2], ) )).id(); commands.spawn(( PlanetSpringJoint { name: planet.name.clone() }, FixedJoint::new(planet_entity, spring).with_point_compliance(planet_config.orbit.planet_spring_compliance) )); } trace!(?planet, "new planet spawned"); } } } } _ => {} } } }