summaryrefslogtreecommitdiffstats
path: root/src/fleet.rs
diff options
context:
space:
mode:
authorJon Santmyer <jon@jonsantmyer.com>2026-05-10 13:29:56 -0400
committerJon Santmyer <jon@jonsantmyer.com>2026-05-10 13:29:56 -0400
commit7f63ec5c10eb7e8dd4edaabd1a6a437328911d39 (patch)
tree36bd3d75ebc0c25256413c21a7cb28c9296953f5 /src/fleet.rs
parentc9041e2e6fe59d6127bb1085b874e8e3cda8000e (diff)
downloadsystemic4x-7f63ec5c10eb7e8dd4edaabd1a6a437328911d39.tar.gz
systemic4x-7f63ec5c10eb7e8dd4edaabd1a6a437328911d39.tar.bz2
systemic4x-7f63ec5c10eb7e8dd4edaabd1a6a437328911d39.zip
fleets
Diffstat (limited to 'src/fleet.rs')
-rw-r--r--src/fleet.rs255
1 files changed, 255 insertions, 0 deletions
diff --git a/src/fleet.rs b/src/fleet.rs
new file mode 100644
index 0000000..39558cd
--- /dev/null
+++ b/src/fleet.rs
@@ -0,0 +1,255 @@
+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 const FLEET_TICK_DURATION: Second = timeman::HOUR;
+
+pub struct Fleet
+{
+ id: FleetId,
+ name: String,
+
+ origin: cgmath::Vector3<Kilometers>,
+ offset: cgmath::Vector3<Kilometers>,
+ heading: cgmath::Vector2<cgmath::Rad<f32>>,
+
+ system: Option<SystemId>,
+ baked_orbit: Option<(f64, StaticOrbit)>,
+}
+
+pub struct FleetsManager
+{
+ next_id: FleetId,
+ fleets: HashMap<FleetId, Fleet>
+}
+
+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 TICK_DURATION: Second = FLEET_TICK_DURATION;
+
+ 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<Kilometers>
+ { &self.origin }
+ pub fn offset_position(&self) -> &cgmath::Vector3<Kilometers>
+ { &self.offset }
+ pub fn heading(&self) -> &cgmath::Vector2<cgmath::Rad<f32>>
+ { &self.heading }
+ pub fn system(&self) -> Option<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 = 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<usize>
+ {
+ self.fleets.keys().cloned().collect()
+ }
+
+ pub fn all_in_system(
+ &self,
+ system: SystemId)
+ -> Vec<usize>
+ {
+ 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>
+ {
+ let tick_interval = Fleet::TICK_DURATION;
+
+ let time_then_offset = time_then % tick_interval;
+ let time_now_offset = time_now % tick_interval;
+
+ let tick_start = time_then + tick_interval - time_then_offset;
+ let tick_end = time_now - time_now_offset;
+
+ let num_ticks = (tick_end - tick_start) / tick_interval;
+ let mut tick_interrupt: Option<Second> = None;
+
+ for tick_i in 0..num_ticks+1 {
+ let tick_time = tick_start + tick_i * tick_interval;
+ if self.subtick_fleet(star_systems, tick_time).is_err() {
+ tick_interrupt = Some(tick_time);
+ }
+ }
+
+ let final_tick = match tick_interrupt {
+ Some(v) => v,
+ None => tick_end
+ };
+
+ if final_tick != tick_end {
+ self.subtick_fleet(star_systems, final_tick);
+ }
+
+ Ok(())
+ }
+} // impl FleetsManager
+
+impl GameState
+{
+ pub fn new_fleet_from_ui(
+ &mut self,
+ info: NewFleet)
+ -> Result<FleetId, Box<dyn Error>>
+ {
+ 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<usize>
+ {
+ fleets_manager.all_in_system(self.id())
+ }
+}