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}; 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: SystemId, 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, system: SystemId) -> 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: system, 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) -> SystemId { 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 = 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]; 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() == 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) } pub fn new_fleet(&mut self, name: String, system: SystemId) -> FleetId { let id = self.reserve_new_id(); self.fleets.insert(id, Fleet::new(id, name, system)); 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 SolarSystem { pub fn fleets( &self, fleets_manager: &FleetsManager) -> Vec { fleets_manager.all_in_system(self.id()) } }