use nalgebra::{vector, Vector2}; use protobuf::SpecialFields; use rapier2d_f64::prelude::{ ColliderBuilder, ColliderSet, RigidBodyBuilder, RigidBodyHandle, RigidBodySet, ActiveEvents, }; use starkingdoms_protocol::planet::PlanetType; use std::collections::HashMap; use crate::entity::{get_entity_id, Entities, Entity, EntityId}; use crate::orbit::constants::{EARTH_MASS, EARTH_RADIUS, MARS_APHELION, MARS_MASS, MARS_PERIHELION, MARS_RADIUS, MOON_APOAPSIS, MOON_MASS, MOON_PERIAPSIS, MOON_RADIUS}; use crate::orbit::orbit::{calculate_point_on_orbit, calculate_world_position_of_orbit}; use crate::{manager::ClientHandlerMessage, SCALE}; pub const GRAVITY: f64 = 0.02; #[derive(Clone, Debug)] pub struct Planet { pub planet_type: PlanetType, pub body_handle: RigidBodyHandle, pub position: (f64, f64), pub radius: f64, pub mass: f64, } impl Planet { pub fn gravity(&self, position: (f64, f64), mass: f64) -> (f64, f64) { let distance = (position.0 - self.position.0).hypot(position.1 - self.position.1); let force = GRAVITY * ((self.mass * mass) / (distance * distance)); let mut direction = Vector2::new(self.position.0 - position.0, self.position.1 - position.1); direction.set_magnitude(force); (direction.x, direction.y) } } #[derive(Default, Clone)] pub struct Planets { pub planets: HashMap, } impl Planets { pub fn get_planet(&self, planet_id: &str) -> Option<&Planet> { self.planets.get(planet_id) } pub fn get_planet_mut(&mut self, planet_id: &str) -> Option<&mut Planet> { self.planets.get_mut(planet_id) } pub fn make_planet( _planet_id: &str, planet_type: PlanetType, mass: f64, radius: f64, position: (f64, f64), rigid_body_set: &mut RigidBodySet, collider_set: &mut ColliderSet, ) -> (EntityId, Entity) { let collider = ColliderBuilder::ball(radius / SCALE).build(); let sensor = ColliderBuilder::ball((radius + 1.) / SCALE) .sensor(true) .active_events(ActiveEvents::COLLISION_EVENTS) .build(); let body = RigidBodyBuilder::kinematic_position_based() .translation(vector![position.0 / SCALE, position.1 / SCALE]) .dominance_group(127) .additional_mass(0.0); let body_handle = rigid_body_set.insert(body); collider_set.insert_with_parent(collider, body_handle, rigid_body_set); collider_set.insert_with_parent(sensor, body_handle, rigid_body_set); let entity_id = get_entity_id(); ( entity_id, Entity::Planet(Planet { planet_type, body_handle, position: (position.0 / SCALE, position.1 / SCALE), radius: radius / SCALE, mass, }), ) } pub fn create_planets( rigid_body_set: &mut RigidBodySet, collider_set: &mut ColliderSet, entities: &mut Entities, ) -> Vec { let mut planet_ids: Vec = Vec::new(); let (earth_id, entity) = Self::make_planet( "earth", PlanetType::Earth, EARTH_MASS, EARTH_RADIUS, (100.0, 100.0), rigid_body_set, collider_set, ); entities.insert(earth_id, entity); planet_ids.push(earth_id); let moon_start_point; #[allow(clippy::expect_used)] if let Entity::Planet(earth) = entities.get(&earth_id).expect("earth does not exist") { moon_start_point = calculate_world_position_of_orbit( calculate_point_on_orbit(MOON_PERIAPSIS, MOON_APOAPSIS, 0.0), vector![earth.position.0, earth.position.1], ); } else { moon_start_point = vector![0., 0.]; } let (moon_id, moon) = Self::make_planet( "moon", PlanetType::Moon, MOON_MASS, MOON_RADIUS, (moon_start_point[0], moon_start_point[1]), rigid_body_set, collider_set, ); entities.insert(moon_id, moon); planet_ids.push(moon_id); let mars_start_point; #[allow(clippy::expect_used)] if let Entity::Planet(moon) = entities.get(&moon_id).expect("moon does not exist") { mars_start_point = calculate_world_position_of_orbit( calculate_point_on_orbit(MARS_PERIHELION, MARS_APHELION, 0.0), vector![moon.position.0, moon.position.1], ); } else { mars_start_point = vector![0., 0.]; } let (mars_id, mars) = Self::make_planet( "mars", PlanetType::Mars, MARS_MASS, MARS_RADIUS, (mars_start_point[0], mars_start_point[1]), rigid_body_set, collider_set, ); entities.insert(mars_id, mars); planet_ids.push(mars_id); planet_ids } pub fn to_protocol(&self) -> ClientHandlerMessage { let mut planets = vec![]; for (_, planet) in self.planets.clone() { // TODO: Adjust codegen to use f64 planets.push(starkingdoms_protocol::planet::Planet { planet_type: planet.planet_type.into(), x: planet.position.0 * SCALE, y: planet.position.1 * SCALE, radius: planet.radius * SCALE, // DO NOT * SCALE - THIS VALUE IS NOT SCALED! YES IT IS special_fields: SpecialFields::default(), }); } ClientHandlerMessage::PlanetData { planets } } pub fn gravity(&self, position: (f64, f64), mass: f64) -> (f64, f64) { let mut direction = Vector2::zeros(); for (_, planet) in self.planets.clone() { let planet_grav = planet.gravity(position, mass); direction.x += planet_grav.0; direction.y += planet_grav.1; } (direction.x, direction.y) } }