use std::cell::RefCell; use std::collections::HashMap; use std::collections::hash_map::Iter; use std::error::Error; use std::iter::Filter; use cgmath::Zero; use crate::GameState; use crate::solar_system::body::BodyId; use crate::solar_system::orbit::StaticOrbiter; use crate::solar_system::{SystemId, GRAVITATIONAL_CONSTANT, Kilometers, SolarSystem, body::OrbitalBody, orbit::StaticOrbit}; use crate::timeman::{self, Second}; use crate::ui::fleet_window::NewFleet; pub type FleetId = usize; pub struct Fleet { id: FleetId, name: String, origin: cgmath::Vector3, offset: cgmath::Vector3, heading: cgmath::Vector2>, system: Option, baked_orbit: Option<(f64, StaticOrbit)>, } pub struct FleetsManager { next_id: FleetId, fleets: HashMap } impl StaticOrbiter for Fleet { fn orbit(&self) -> Option<&StaticOrbit> { match &self.baked_orbit { Some((_, orbit)) => { Some(orbit) }, None => None } } fn sgp(&self) -> f64 { match &self.baked_orbit { Some((sgp, _)) => { *sgp }, None => { 0.0 } } } } impl Fleet { pub fn new( id: FleetId, name: String) -> Self { Self { id, name, origin: cgmath::vec3(0.0, 0.0, 0.0), offset: cgmath::vec3(0.0, 0.0, 0.0), heading: cgmath::vec2( cgmath::Rad::zero(), cgmath::Rad::zero()), system: None, baked_orbit: None, } } pub fn id(&self) -> FleetId { self.id } pub fn name(&self) -> &String { &self.name } pub fn origin_position(&self) -> &cgmath::Vector3 { &self.origin } pub fn offset_position(&self) -> &cgmath::Vector3 { &self.offset } pub fn heading(&self) -> &cgmath::Vector2> { &self.heading } pub fn system(&self) -> Option { self.system } pub fn make_orbit( &mut self, star_system: &SolarSystem, body: BodyId, radius: Kilometers) { let body = star_system.body(body); let sgp = body.mass() * GRAVITATIONAL_CONSTANT; self.baked_orbit = Some((sgp, StaticOrbit::new_circular(body, radius))); self.system = Some(star_system.id()); } fn subtick( &mut self, star_systems: &[SolarSystem], time: Second) -> Result<(), ()> { if let Some((_sgp, orbit)) = &self.baked_orbit { let solar_system = &star_systems[self.system().unwrap()]; let body = solar_system.body(orbit.parent()); self.origin = body.absolute_position(solar_system, time); self.offset = orbit.calculate_position_at(self, time); } Ok(()) } } // impl Fleet impl FleetsManager { pub fn new() -> Self { Self { next_id: 0, fleets: HashMap::new() } } fn reserve_new_id(&mut self) -> FleetId { let new_id = self.next_id; self.next_id += 1; new_id } pub fn all(&self) -> Vec { self.fleets.keys().cloned().collect() } pub fn all_in_system( &self, system: SystemId) -> Vec { self.fleets.iter().filter_map(|v| { if v.1.system().is_some_and(|id| { id == system }) { Some(*v.0) }else { None } }).collect() } pub fn entry_mut( &mut self, id: FleetId) -> Option<&mut Fleet> { self.fleets.get_mut(&id) } pub fn entry( &self, id: FleetId) -> Option<&Fleet> { self.fleets.get(&id) } fn subtick_fleet( &mut self, star_systems: &[SolarSystem], time: Second) -> Result<(), ()> { let mut tick_interrupt = Ok(()); for (id, fleet) in &mut self.fleets { let res = fleet.subtick(star_systems, time); if res.is_err() { tick_interrupt = Err(()); } } tick_interrupt } pub fn tick( &mut self, star_systems: &[SolarSystem], time_then: Second, time_now: Second) -> Result<(), Second> { self.subtick_fleet(star_systems, time_now); Ok(()) } } // impl FleetsManager impl GameState { pub fn new_fleet_from_ui( &mut self, info: NewFleet) -> Result> { let star_system = &self.solar_systems[info.system]; let fleet_id = self.fleets.reserve_new_id(); let mut fleet = Fleet::new(fleet_id, info.name); fleet.make_orbit(star_system, info.orbiting, info.sma); self.fleets.fleets.insert( fleet_id, fleet); Ok(fleet_id) } } impl SolarSystem { pub fn fleets( &self, fleets_manager: &FleetsManager) -> Vec { fleets_manager.all_in_system(self.id()) } }