pub mod schedule; use std::collections::{BinaryHeap, HashMap}; use std::error::Error; use std::ops::{Range, RangeBounds}; use cgmath::Zero; use crate::GameState; use crate::fleet::schedule::{Schedule, ScheduleManager}; use crate::solar_system::body::BodyId; use crate::solar_system::orbit::StaticOrbiter; use crate::solar_system::{SystemId, GRAVITATIONAL_CONSTANT, Kilometers, SolarSystem, orbit::StaticOrbit}; use crate::timeman::{HOUR, 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>, velocity: cgmath::Vector3, acceleration: cgmath::Vector3, angular_velocity: cgmath::Vector2, angular_acceleration: 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 const SUBTICK_DURATION: Second = HOUR; 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()), velocity: cgmath::vec3(0.0, 0.0, 0.0), acceleration: cgmath::vec3(0.0, 0.0, 0.0), angular_velocity: cgmath::vec2(0.0, 0.0), angular_acceleration: cgmath::vec2(0.0, 0.0), 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 tick( &mut self, star_systems: &[SolarSystem], time: &Range) { 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.end); self.offset = orbit.calculate_position_at(self, time.end); }else{ } } } // 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.values().filter_map(|v| { if v.system().is_some_and(|id| { id == system }) { Some(v.id()) }else { None } }).collect() } pub fn fleet_mut(&mut self, id: FleetId) -> Option<&mut Fleet> { self.fleets.get_mut(&id) } pub fn fleet(&self, id: FleetId) -> Option<&Fleet> { self.fleets.get(&id) } fn subtick( &mut self, star_systems: &[SolarSystem], scheduler: &mut ScheduleManager, time: &Range) -> Result<(), Second> { let interrupt = scheduler.subtick(self, star_systems, time); let new_range = time.start.. (if interrupt.is_err() { interrupt.expect_err("Expect interrupt") } else { time.end } ); for (_, fleet) in self.fleets.iter_mut() { fleet.tick(star_systems, &new_range); } interrupt } pub fn tick( &mut self, star_systems: &[SolarSystem], scheduler: &mut ScheduleManager, time: Range) -> Result<(), Second> { let delta_time = time.end - time.start; let subticks = delta_time / Fleet::SUBTICK_DURATION; for tick in 0..subticks { let last_tick_time = time.start + tick * Fleet::SUBTICK_DURATION; let now_tick_time = last_tick_time + Fleet::SUBTICK_DURATION; let tick_time = last_tick_time..now_tick_time; let interrupt = self.subtick(star_systems, scheduler, &tick_time); if interrupt.is_err() { return interrupt; } } let last_tick_start = time.start + subticks * Fleet::SUBTICK_DURATION; let last_tick_time = last_tick_start..time.end; self.subtick(star_systems, scheduler, &last_tick_time) } } // 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()) } }