diff --git a/Cargo.lock b/Cargo.lock index 49edf44..d9ab5bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -699,6 +699,7 @@ dependencies = [ "galactica-galaxy", "galactica-util", "pollster", + "rapier2d", "wgpu", "winit", ] diff --git a/crates/content/src/part/system.rs b/crates/content/src/part/system.rs index d210be8..b95432e 100644 --- a/crates/content/src/part/system.rs +++ b/crates/content/src/part/system.rs @@ -207,7 +207,7 @@ impl crate::Build for System { }); } - objects.sort_by(|a, b| a.pos.z.total_cmp(&b.pos.z)); + objects.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z)); content.systems.push(Self { name: system_name, objects, diff --git a/crates/galactica/src/game.rs b/crates/galactica/src/game.rs index e790f26..8563e8f 100644 --- a/crates/galactica/src/game.rs +++ b/crates/galactica/src/game.rs @@ -1,15 +1,14 @@ use cgmath::Point2; use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle}; -use galactica_galaxy::{ship::ShipPersonality, Galaxy, GxShipHandle}; +use galactica_galaxy::ship::ShipPersonality; use galactica_playeragent::PlayerAgent; -use galactica_systemsim::{ParticleBuilder, StepResources, SystemSim, Wrapper}; +use galactica_systemsim::{ParticleBuilder, StepResources, SySimShipHandle, SystemSim, Wrapper}; use galactica_util::timing::Timing; use rand::seq::SliceRandom; use std::time::Instant; #[derive(Clone)] pub struct GameState { - pub gx: Galaxy, pub systemsim: SystemSim, pub timing: Timing, pub start_instant: Instant, @@ -37,57 +36,56 @@ pub struct Game { unsafe impl<'a> Send for Game {} impl<'a> Game { - pub fn make_player(&mut self) -> GxShipHandle { - let player = self.state.gx.create_ship( + pub fn make_player(&mut self) -> SySimShipHandle { + let player = self.state.systemsim.add_ship( &self.ct, ShipHandle { index: 0 }, FactionHandle { index: 0 }, ShipPersonality::Player, - &SystemHandle { index: 0 }, + Point2 { x: 0.0, y: 0.0 }, ); - let s = self.state.gx.get_ship_mut(player).unwrap(); - s.add_outfit(&self.ct.get_outfit(OutfitHandle { index: 0 })); - s.add_outfit(&self.ct.get_outfit(OutfitHandle { index: 1 })); - s.add_outfit(&self.ct.get_outfit(OutfitHandle { index: 2 })); - self.state - .systemsim - .add_ship(&self.ct, &s, Point2 { x: 0.0, y: 0.0 }); + let s = self.state.systemsim.get_ship_mut(&player).unwrap(); + s.data + .add_outfit(&self.ct.get_outfit(OutfitHandle { index: 0 })); + s.data + .add_outfit(&self.ct.get_outfit(OutfitHandle { index: 1 })); + s.data + .add_outfit(&self.ct.get_outfit(OutfitHandle { index: 2 })); return player; } pub fn new(ct: Content) -> Self { - let mut gx = Galaxy::new(&ct); + let mut systemsim = SystemSim::new(&ct, SystemHandle { index: 0 }); - let a = gx.create_ship( + let a = systemsim.add_ship( &ct, ShipHandle { index: 0 }, FactionHandle { index: 1 }, - ShipPersonality::Dummy, - &SystemHandle { index: 0 }, + ShipPersonality::Point, + Point2 { x: 100.0, y: 0.0 }, ); - let s = gx.get_ship_mut(a).unwrap(); - s.add_outfit(&ct.get_outfit(OutfitHandle { index: 0 })); - s.add_outfit(&ct.get_outfit(OutfitHandle { index: 1 })); - s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 })); - let a = gx.create_ship( + let s = systemsim.get_ship_mut(&a).unwrap(); + s.data.add_outfit(ct.get_outfit(OutfitHandle { index: 0 })); + s.data.add_outfit(&ct.get_outfit(OutfitHandle { index: 1 })); + s.data.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 })); + + let a = systemsim.add_ship( &ct, ShipHandle { index: 0 }, FactionHandle { index: 0 }, ShipPersonality::Point, - &SystemHandle { index: 0 }, + Point2 { x: 0.0, y: 120.0 }, ); - let s = gx.get_ship_mut(a).unwrap(); - s.add_outfit(&ct.get_outfit(OutfitHandle { index: 0 })); - s.add_outfit(&ct.get_outfit(OutfitHandle { index: 1 })); - s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 })); - let systemsim = SystemSim::new(&ct, &gx, SystemHandle { index: 0 }); + let s = systemsim.get_ship_mut(&a).unwrap(); + s.data.add_outfit(ct.get_outfit(OutfitHandle { index: 0 })); + s.data.add_outfit(&ct.get_outfit(OutfitHandle { index: 1 })); + s.data.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 })); let state = GameState { - gx, systemsim, timing: Timing::new(), start_instant: Instant::now(), @@ -121,13 +119,8 @@ impl<'a> Game { let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale; self.new_particles.clear(); - self.state.timing.start_galaxy(); - self.state.gx.step(t); - self.state.timing.mark_galaxy(); - self.state.systemsim.step(StepResources { ct: &self.ct, - gx: &mut self.state.gx, particles: &mut self.new_particles, timing: &mut self.state.timing, wrapper: &mut self.wrapper, diff --git a/crates/galactica/src/main.rs b/crates/galactica/src/main.rs index a8d078e..6c71644 100644 --- a/crates/galactica/src/main.rs +++ b/crates/galactica/src/main.rs @@ -4,7 +4,7 @@ use anyhow::{bail, Result}; use galactica_content::{Content, SystemHandle}; use galactica_playeragent::{PlayerAgent, PlayerStatus}; use galactica_render::RenderInput; -use galactica_systemsim::util; +use galactica_systemsim::{util, SySimShipHandle}; use galactica_util::constants::ASSET_CACHE; use std::{ fs, @@ -41,7 +41,7 @@ fn main() -> Result<()> { let mut game = game::Game::new(content.clone()); let p = game.make_player(); - let mut player = PlayerAgent::new(p); + let mut player = PlayerAgent::new(p.0); player.set_camera_aspect( gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32, ); @@ -56,8 +56,7 @@ fn main() -> Result<()> { ct: &content, systemsim: &game.get_state().systemsim, particles: game.get_particles(), - player_data: player.ship.unwrap(), - gx: &game.get_state().gx, + player_ship: SySimShipHandle(player.ship.unwrap()), current_system: SystemHandle { index: 0 }, timing: game.get_state().timing.clone(), }; @@ -77,7 +76,10 @@ fn main() -> Result<()> { let player_status = { let pos = { - let o = &game.get_state().systemsim.get_ship(player.ship.unwrap()); + let o = &game + .get_state() + .systemsim + .get_ship(&SySimShipHandle(player.ship.unwrap())); if let Some(o) = o { let r = &game.get_state().systemsim.get_rigid_body(o.rigid_body); if let Some(r) = r { diff --git a/crates/galaxy/src/galaxy.rs b/crates/galaxy/src/galaxy.rs deleted file mode 100644 index 1a4cf64..0000000 --- a/crates/galaxy/src/galaxy.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::collections::HashMap; - -use crate::{handles::GxShipHandle, ship::GxShip, ship::ShipPersonality}; -use galactica_content::{Content, FactionHandle, ShipHandle, SystemHandle}; - -/// Keeps track of all objects in the galaxy. -/// This struct does NO physics, it keeps track of data exclusively. -#[derive(Debug, Clone)] -pub struct Galaxy { - /// Universal counter. - /// Used to create unique handles for game objects. - index: u64, - - /// All ships in the galaxy - ships: HashMap, - - /// Ships indexed by the system they're in. - /// A ship must always be in exactly one system. - system_ship_table: HashMap>, - - /// Systems indexed by which ships they contain. - /// A ship must always be in exactly one system. - ship_system_table: HashMap, -} - -impl Galaxy { - pub fn new(ct: &Content) -> Self { - Self { - system_ship_table: ct.iter_systems().map(|s| (s, Vec::new())).collect(), - ship_system_table: HashMap::new(), - index: 0, - ships: HashMap::new(), - } - } - - /// Spawn a ship - pub fn create_ship( - &mut self, - ct: &Content, - ship: ShipHandle, - faction: FactionHandle, - personality: ShipPersonality, - system: &SystemHandle, - ) -> GxShipHandle { - let handle = GxShipHandle { - index: self.index, - content: ship, - }; - self.index += 1; - - self.ships - .insert(handle, GxShip::new(ct, handle, ship, faction, personality)); - self.system_ship_table.get_mut(system).unwrap().push(handle); - self.ship_system_table.insert(handle, *system); - - return handle; - } - - pub fn step(&mut self, t: f32) { - // TODO: don't allocate on step, need a better - // way to satisfy the borrow checker. - // Same needs to be done in the `systemsim` crate. - let mut to_remove = Vec::new(); - for (_, s) in &mut self.ships { - s.step(t); - - if s.is_dead() { - to_remove.push(s.get_handle()); - } - } - - // Remove dead ships - // No fancy animation here, that's handled by the physics system. - for i in to_remove { - let system = self.ship_system_table.remove(&i).unwrap(); - self.system_ship_table - .get_mut(&system) - .unwrap() - .retain(|x| *x != i); - self.ships.remove(&i); - } - } -} - -// Public getters -impl Galaxy { - pub fn get_ship(&self, handle: GxShipHandle) -> Option<&GxShip> { - self.ships.get(&handle) - } - - pub fn get_ship_mut(&mut self, handle: GxShipHandle) -> Option<&mut GxShip> { - self.ships.get_mut(&handle) - } - - pub fn iter_ships(&self) -> impl Iterator { - self.ships.values() - } -} diff --git a/crates/galaxy/src/handles.rs b/crates/galaxy/src/handles.rs deleted file mode 100644 index bc51e3f..0000000 --- a/crates/galaxy/src/handles.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::hash::Hash; - -use galactica_content::ShipHandle; - -/// A lightweight representation of a ship in the galaxy -#[derive(Debug, Clone, Copy)] -pub struct GxShipHandle { - /// This ship's unique index - pub(crate) index: u64, - - /// This ship's content handle - /// (technically redundant, but this greatly simplifies code) - pub(crate) content: ShipHandle, -} - -impl GxShipHandle { - pub fn content_handle(&self) -> ShipHandle { - self.content - } -} - -impl Hash for GxShipHandle { - fn hash(&self, state: &mut H) { - self.index.hash(state) - } -} - -impl Eq for GxShipHandle {} -impl PartialEq for GxShipHandle { - fn eq(&self, other: &Self) -> bool { - self.index.eq(&other.index) - } -} diff --git a/crates/galaxy/src/lib.rs b/crates/galaxy/src/lib.rs index 4696b55..05271e4 100644 --- a/crates/galaxy/src/lib.rs +++ b/crates/galaxy/src/lib.rs @@ -1,11 +1 @@ -//! This module keeps track of the galaxy state. -//! It is also responsible for ship stats, outfits, etc. -//! -//! `galaxy` does not provide any simulation logic---that is done in seperate crates. - -mod galaxy; -mod handles; pub mod ship; - -pub use galaxy::*; -pub use handles::*; diff --git a/crates/galaxy/src/ship/ship.rs b/crates/galaxy/src/ship/ship.rs index d25e5ea..4dddee6 100644 --- a/crates/galaxy/src/ship/ship.rs +++ b/crates/galaxy/src/ship/ship.rs @@ -1,21 +1,75 @@ use std::{collections::HashMap, time::Instant}; -use crate::GxShipHandle; - use super::{OutfitSet, ShipPersonality}; use galactica_content::{Content, FactionHandle, GunPoint, Outfit, ShipHandle}; use rand::{rngs::ThreadRng, Rng}; +/// Ship state machine. +/// Any ship we keep track of is in one of these states. +/// Dead ships don't exist---they removed once their collapse +/// sequence fully plays out. +#[derive(Debug, Clone)] +pub enum ShipState { + /// This ship is alive and well in open space + Flying, // TODO: system, position (also in collapse)? + + /// This ship has been destroyed, and is playing its collapse sequence. + /// Parameters are total collapse sequence length and remaining sequence length, in seconds. + Collapsing { total: f32, elapsed: f32 }, +} + +impl ShipState { + /// True if this ship has been destroyed and has finished it's collapse sequence. + /// Ships are deleted once this is true. + pub fn should_be_removed(&self) -> bool { + match self { + Self::Collapsing { elapsed, total } => elapsed >= total, + _ => false, + } + } + + /// Is this ship playing its collapse sequence? + pub fn is_collapsing(&self) -> bool { + match self { + Self::Collapsing { .. } => true, + _ => false, + } + } + + /// If this ship is collapsing, return total collapse time and remaining collapse time. + /// Otherwise, return None + pub fn collapse_state(&self) -> Option<(f32, f32)> { + match self { + Self::Collapsing { + total, + elapsed: remaining, + } => Some((*total, *remaining)), + _ => None, + } + } + + /// Is this ship flying in open space? + pub fn is_flying(&self) -> bool { + match self { + Self::Flying => true, + _ => false, + } + } +} + #[derive(Debug, Clone)] pub struct GxShip { // Metadata values - handle: GxShipHandle, ct_handle: ShipHandle, faction: FactionHandle, outfits: OutfitSet, personality: ShipPersonality, + /// Ship state machine. Keeps track of all possible ship state. + /// TODO: document this, draw a graph + state: ShipState, + // State values // TODO: unified ship stats struct, like outfit space hull: f32, @@ -29,16 +83,14 @@ pub struct GxShip { } impl GxShip { - pub(crate) fn new( + pub fn new( ct: &Content, - handle: GxShipHandle, ct_handle: ShipHandle, faction: FactionHandle, personality: ShipPersonality, ) -> Self { let s = ct.get_ship(ct_handle); GxShip { - handle, ct_handle, faction, outfits: OutfitSet::new(s.space, &s.guns), @@ -46,6 +98,9 @@ impl GxShip { last_hit: Instant::now(), rng: rand::thread_rng(), + // TODO: ships must always start landed on planets + state: ShipState::Flying, + // Initial stats hull: s.hull, shields: 0.0, @@ -65,11 +120,6 @@ impl GxShip { self.outfits.remove(o) } - /// If this ship is dead, it will be removed from the game. - pub fn is_dead(&self) -> bool { - self.hull <= 0.0 - } - /// Try to fire a gun. /// Will panic if `which` isn't a point on this ship. /// Returns `true` if this gun was fired, @@ -93,8 +143,8 @@ impl GxShip { } /// Hit this ship with the given amount of damage - pub fn apply_damage(&mut self, mut d: f32) { - if self.is_dead() { + pub fn apply_damage(&mut self, ct: &Content, mut d: f32) { + if self.state.is_collapsing() { return; } if self.shields >= d { @@ -105,26 +155,44 @@ impl GxShip { self.hull = 0f32.max(self.hull - d); } self.last_hit = Instant::now(); + + if self.hull <= 0.0 { + // This ship has been destroyed, update state + self.state = ShipState::Collapsing { + total: ct.get_ship(self.ct_handle).collapse.length, + elapsed: 0.0, + } + } } /// Update this ship's state by `t` seconds pub fn step(&mut self, t: f32) { - // Cooldown guns - for (_, c) in &mut self.gun_cooldowns { - if *c > 0.0 { - *c -= t; + match self.state { + ShipState::Collapsing { + ref mut elapsed, .. + } => { + *elapsed += t; } - } - // Regenerate shields - let time_since = self.last_hit.elapsed().as_secs_f32(); - if self.shields != self.outfits.get_shield_strength() { - for g in self.outfits.iter_shield_generators() { - if time_since >= g.delay { - self.shields += g.generation * t; - if self.shields > self.outfits.get_shield_strength() { - self.shields = self.outfits.get_shield_strength(); - break; + ShipState::Flying => { + // Cooldown guns + for (_, c) in &mut self.gun_cooldowns { + if *c > 0.0 { + *c -= t; + } + } + + // Regenerate shields + let time_since = self.last_hit.elapsed().as_secs_f32(); + if self.shields != self.outfits.get_shield_strength() { + for g in self.outfits.iter_shield_generators() { + if time_since >= g.delay { + self.shields += g.generation * t; + if self.shields > self.outfits.get_shield_strength() { + self.shields = self.outfits.get_shield_strength(); + break; + } + } } } } @@ -134,9 +202,9 @@ impl GxShip { // Misc getters, so internal state is untouchable impl GxShip { - /// Get a handle to this ship game object - pub fn get_handle(&self) -> GxShipHandle { - self.handle + /// Get this ship's state + pub fn get_state(&self) -> &ShipState { + &self.state } /// Get a handle to this ship's content diff --git a/crates/playeragent/Cargo.toml b/crates/playeragent/Cargo.toml index ed75843..240bee9 100644 --- a/crates/playeragent/Cargo.toml +++ b/crates/playeragent/Cargo.toml @@ -26,3 +26,4 @@ wgpu = { workspace = true } pollster = { workspace = true } anyhow = { workspace = true } cgmath = { workspace = true } +rapier2d = { workspace = true } diff --git a/crates/playeragent/src/playeragent.rs b/crates/playeragent/src/playeragent.rs index 2da8615..4483da1 100644 --- a/crates/playeragent/src/playeragent.rs +++ b/crates/playeragent/src/playeragent.rs @@ -1,12 +1,12 @@ use galactica_content::Content; -use galactica_galaxy::GxShipHandle; +use rapier2d::geometry::ColliderHandle; use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode}; use crate::{camera::Camera, inputstatus::InputStatus, PlayerStatus}; pub struct PlayerAgent { /// Which ship this player is controlling - pub ship: Option, + pub ship: Option, /// This player's camera pub camera: Camera, @@ -19,7 +19,7 @@ pub struct PlayerAgent { } impl PlayerAgent { - pub fn new(ship: GxShipHandle) -> Self { + pub fn new(ship: ColliderHandle) -> Self { Self { input: InputStatus::new(), camera: Camera::new(), diff --git a/crates/render/src/datastructs.rs b/crates/render/src/datastructs.rs index 184cce7..00920ac 100644 --- a/crates/render/src/datastructs.rs +++ b/crates/render/src/datastructs.rs @@ -1,7 +1,6 @@ use cgmath::Point2; use galactica_content::{Content, SystemHandle}; -use galactica_galaxy::{Galaxy, GxShipHandle}; -use galactica_systemsim::{ParticleBuilder, SystemSim}; +use galactica_systemsim::{ParticleBuilder, SySimShipHandle, SystemSim}; use galactica_util::timing::Timing; use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer}; use std::rc::Rc; @@ -16,7 +15,7 @@ pub struct RenderInput<'a> { pub camera_pos: Point2, /// Player ship data - pub player_data: GxShipHandle, + pub player_ship: SySimShipHandle, /// The system we're currently in pub current_system: SystemHandle, @@ -34,9 +33,6 @@ pub struct RenderInput<'a> { /// Game content pub ct: &'a Content, - /// Game data - pub gx: &'a Galaxy, - /// Particles to spawn during this frame pub particles: &'a Vec, diff --git a/crates/render/src/gpustate/systemsim.rs b/crates/render/src/gpustate/systemsim.rs index a9b9cfa..fb08700 100644 --- a/crates/render/src/gpustate/systemsim.rs +++ b/crates/render/src/gpustate/systemsim.rs @@ -18,12 +18,12 @@ impl GPUState { // NE and SW corners of screen screen_clip: (Point2, Point2), ) { - for s in state.systemsim.iter_ships() { - let r = state.systemsim.get_rigid_body(s.rigid_body).unwrap(); + for ship in state.systemsim.iter_ships() { + let r = state.systemsim.get_rigid_body(ship.rigid_body).unwrap(); let ship_pos = util::rigidbody_position(&r); let ship_rot = util::rigidbody_rotation(r); let ship_ang = -ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }); // TODO: inconsistent angles. Fix! - let ship_cnt = state.ct.get_ship(s.data_handle.content_handle()); + let ship_cnt = state.ct.get_ship(ship.data.get_content()); // Position adjusted for parallax // TODO: adjust parallax for zoom? @@ -80,16 +80,8 @@ impl GPUState { ); self.state.vertex_buffers.object_counter += 1; - // This will be None if this ship is dead. - // (physics object stays around to complete the death animation) - // If that is the case, we're done, no flares to draw anyway! - let ship = match state.gx.get_ship(s.data_handle) { - None => continue, - Some(s) => s, - }; - - let flare = ship.get_outfits().get_flare_sprite(state.ct); - if s.get_controls().thrust && flare.is_some() { + let flare = ship.data.get_outfits().get_flare_sprite(state.ct); + if ship.get_controls().thrust && flare.is_some() && ship.data.get_state().is_flying() { for engine_point in &ship_cnt.engines { self.state.queue.write_buffer( &self.state.global_uniform.object_buffer, diff --git a/crates/render/src/ui/fpsindicator.rs b/crates/render/src/ui/fpsindicator.rs index 2b78cb6..93ab9c0 100644 --- a/crates/render/src/ui/fpsindicator.rs +++ b/crates/render/src/ui/fpsindicator.rs @@ -36,9 +36,9 @@ impl FpsIndicator { self.buffer.set_text( &mut state.text_font_system, &format!( - "Frame: {:#?} ({:05.00})\nGame: {:05.02}%\nShips: {:05.02}%\nPhys: {:05.02}%\n", - input.timing.frame, 1.0 /input.timing.frame.as_secs_f32(), - 100.0 * (input.timing.galaxy.as_secs_f32() / input.timing.frame.as_secs_f32()), + "Frame: {:#?} ({:05.00})\nShips: {:05.02}%\nPhys: {:05.02}%\n", + input.timing.frame, + 1.0 / input.timing.frame.as_secs_f32(), 100.0 * (input.timing.physics_sim.as_secs_f32() / input.timing.frame.as_secs_f32()), 100.0 * (input.timing.physics_ship.as_secs_f32() / input.timing.frame.as_secs_f32()), diff --git a/crates/render/src/ui/radar.rs b/crates/render/src/ui/radar.rs index f9df156..616fd27 100644 --- a/crates/render/src/ui/radar.rs +++ b/crates/render/src/ui/radar.rs @@ -1,4 +1,5 @@ use cgmath::{Deg, InnerSpace, Point2, Rad, Vector2}; +use galactica_galaxy::ship::ShipState; use galactica_systemsim::util; use galactica_util::constants::UI_SPRITE_INSTANCE_LIMIT; @@ -31,13 +32,17 @@ impl Radar { // TODO: maybe a cleaner solution for last posititon? // This is necessary because the player may be dead or landed - if let Some(player_world_object) = input.systemsim.get_ship(input.player_data) { - let player_body = input - .systemsim - .get_rigid_body(player_world_object.rigid_body) - .unwrap(); + let player_ship = input.systemsim.get_ship(&input.player_ship).unwrap(); - self.last_player_position = util::rigidbody_position(player_body) + match player_ship.data.get_state() { + ShipState::Flying | ShipState::Collapsing { .. } => { + let player_body = input + .systemsim + .get_rigid_body(player_ship.rigid_body) + .unwrap(); + + self.last_player_position = util::rigidbody_position(player_body) + } }; let planet_sprite = input.ct.get_sprite_handle("ui::planetblip"); @@ -115,17 +120,17 @@ impl Radar { for (s, r) in input.systemsim.iter_ship_body() { // This will be None if this ship is dead. // Stays around while the physics system runs a collapse sequence - let color = match input.gx.get_ship(s.data_handle) { - None => { + let color = match s.data.get_state() { + ShipState::Collapsing { .. } => { // TODO: configurable [0.2, 0.2, 0.2, 1.0] } - Some(data) => { - let c = input.ct.get_faction(data.get_faction()).color; + ShipState::Flying => { + let c = input.ct.get_faction(s.data.get_faction()).color; [c[0], c[1], c[2], 1.0] } }; - let ship = input.ct.get_ship(s.data_handle.content_handle()); + let ship = input.ct.get_ship(s.data.get_content()); let size = (ship.size * ship.sprite.aspect) * ship_scale; let p = util::rigidbody_position(r); let d = (p - self.last_player_position) / radar_range; diff --git a/crates/render/src/ui/status.rs b/crates/render/src/ui/status.rs index 404d3e7..5ea1c79 100644 --- a/crates/render/src/ui/status.rs +++ b/crates/render/src/ui/status.rs @@ -29,19 +29,17 @@ impl Status { let current_shields; let current_hull; let max_hull; - if let Some(player_world_object) = input.systemsim.get_ship(input.player_data) { - let data = input.gx.get_ship(player_world_object.data_handle).unwrap(); - max_shields = data.get_outfits().get_shield_strength(); - current_shields = data.get_shields(); - current_hull = data.get_hull(); - max_hull = input.ct.get_ship(data.get_content()).hull; - } else { - // Sensible defaults if ship doesn't exist (landed or dead) - max_shields = 1.0; - current_shields = 0.0; - current_hull = 0.0; - max_hull = 1.0; - }; + let player_ship = input.systemsim.get_ship(&input.player_ship).unwrap(); + + match player_ship.data.get_state() { + galactica_galaxy::ship::ShipState::Collapsing { .. } + | galactica_galaxy::ship::ShipState::Flying => { + max_shields = player_ship.data.get_outfits().get_shield_strength(); + current_shields = player_ship.data.get_shields(); + current_hull = player_ship.data.get_hull(); + max_hull = input.ct.get_ship(player_ship.data.get_content()).hull; + } + } state.queue.write_buffer( &state.vertex_buffers.ui.instances, diff --git a/crates/systemsim/src/controller/mod.rs b/crates/systemsim/src/controller/mod.rs index 61b6abd..7b8a9fa 100644 --- a/crates/systemsim/src/controller/mod.rs +++ b/crates/systemsim/src/controller/mod.rs @@ -6,8 +6,7 @@ mod point; use null::*; use point::PointShipController; -use galactica_galaxy::GxShipHandle; -use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet}; +use rapier2d::{dynamics::RigidBodySet, geometry::ColliderHandle}; use std::{collections::HashMap, fmt::Debug}; use crate::{ @@ -41,13 +40,12 @@ impl ShipController { &mut self, res: &StepResources, rigid_bodies: &RigidBodySet, - ships: &HashMap, - this_ship: RigidBodyHandle, - this_data: GxShipHandle, + ships: &HashMap, + this_ship: ColliderHandle, ) -> Option { match self { - Self::Null(n) => n.update_controls(res, rigid_bodies, ships, this_ship, this_data), - Self::Point(p) => p.update_controls(res, rigid_bodies, ships, this_ship, this_data), + Self::Null(n) => n.update_controls(res, rigid_bodies, ships, this_ship), + Self::Point(p) => p.update_controls(res, rigid_bodies, ships, this_ship), } } } @@ -65,8 +63,7 @@ where &mut self, res: &StepResources, rigid_bodies: &RigidBodySet, - ships: &HashMap, - this_ship: RigidBodyHandle, - this_data: GxShipHandle, + ships: &HashMap, + this_ship: ColliderHandle, ) -> Option; } diff --git a/crates/systemsim/src/controller/null.rs b/crates/systemsim/src/controller/null.rs index 54da163..eba28fd 100644 --- a/crates/systemsim/src/controller/null.rs +++ b/crates/systemsim/src/controller/null.rs @@ -1,5 +1,4 @@ -use galactica_galaxy::GxShipHandle; -use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet}; +use rapier2d::{dynamics::RigidBodySet, geometry::ColliderHandle}; use std::collections::HashMap; use super::ShipControllerStruct; @@ -25,9 +24,8 @@ impl ShipControllerStruct for NullShipController { &mut self, _res: &StepResources, _rigid_bodies: &RigidBodySet, - _ships: &HashMap, - _this_ship: RigidBodyHandle, - _this_data: GxShipHandle, + _ships: &HashMap, + _this_ship: ColliderHandle, ) -> Option { None } diff --git a/crates/systemsim/src/controller/point.rs b/crates/systemsim/src/controller/point.rs index 00ffc97..2a4828f 100644 --- a/crates/systemsim/src/controller/point.rs +++ b/crates/systemsim/src/controller/point.rs @@ -1,7 +1,6 @@ use cgmath::{Deg, InnerSpace}; use galactica_content::Relationship; -use galactica_galaxy::GxShipHandle; -use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet}; +use rapier2d::{dynamics::RigidBodySet, geometry::ColliderHandle}; use std::collections::HashMap; use super::ShipControllerStruct; @@ -27,35 +26,28 @@ impl ShipControllerStruct for PointShipController { &mut self, res: &StepResources, rigid_bodies: &RigidBodySet, - ships: &HashMap, - this_ship: RigidBodyHandle, - this_data: GxShipHandle, + ships: &HashMap, + this_ship: ColliderHandle, ) -> Option { let mut controls = ShipControls::new(); - let this_rigidbody = rigid_bodies.get(this_ship).unwrap(); - let my_data = res.gx.get_ship(this_data).unwrap(); + let my_ship = ships.get(&this_ship).unwrap(); + let this_rigidbody = rigid_bodies.get(my_ship.rigid_body).unwrap(); let my_position = util::rigidbody_position(this_rigidbody); let my_rotation = util::rigidbody_rotation(this_rigidbody); let my_angvel = this_rigidbody.angvel(); - let my_faction = res.ct.get_faction(my_data.get_faction()); + let my_faction = res.ct.get_faction(my_ship.data.get_faction()); // Iterate all possible targets let mut hostile_ships = ships .values() - .filter(|s| { - let data = res.gx.get_ship(s.data_handle); - if let Some(data) = data { - match my_faction.relationships.get(&data.get_faction()).unwrap() { - Relationship::Hostile => true, - _ => false, - } - } else { - // This check is necessary---we don't want to target (or panic on) - // ships that don't have data (and are thus playing their collapse animation) - false - } - }) + .filter( + // Target only flying ships we're hostile towards + |s| match my_faction.relationships.get(&s.data.get_faction()).unwrap() { + Relationship::Hostile => s.data.get_state().is_flying(), + _ => false, + }, + ) .map(|s| rigid_bodies.get(s.rigid_body).unwrap()); // Find the closest target diff --git a/crates/systemsim/src/lib.rs b/crates/systemsim/src/lib.rs index e289510..d759dd5 100644 --- a/crates/systemsim/src/lib.rs +++ b/crates/systemsim/src/lib.rs @@ -12,5 +12,5 @@ mod wrapper; pub use particlebuilder::*; pub use stepresources::*; -pub use systemsim::SystemSim; +pub use systemsim::{SySimShipHandle, SystemSim}; pub use wrapper::Wrapper; diff --git a/crates/systemsim/src/objects/collapse.rs b/crates/systemsim/src/objects/collapse.rs index c8be78e..ee63226 100644 --- a/crates/systemsim/src/objects/collapse.rs +++ b/crates/systemsim/src/objects/collapse.rs @@ -1,5 +1,6 @@ use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero}; -use galactica_content::{CollapseEvent, Ship, ShipHandle}; +use galactica_content::{CollapseEvent, Ship}; +use galactica_galaxy::ship::GxShip; use nalgebra::point; use rand::{rngs::ThreadRng, Rng}; use rapier2d::{dynamics::RigidBody, geometry::Collider}; @@ -8,34 +9,29 @@ use crate::{util, ParticleBuilder, StepResources}; #[derive(Debug, Clone)] pub(super) struct ShipCollapseSequence { - total_length: f32, - time_elapsed: f32, rng: ThreadRng, + + /// The elapsed collapse duration when step() + /// was last called + last_call: f32, } impl ShipCollapseSequence { - pub(super) fn new(total_length: f32) -> Self { + pub(super) fn new() -> Self { Self { - total_length, - time_elapsed: 0.0, rng: rand::thread_rng(), + last_call: 0.0, } } - /// Has this sequence been fully played out? - pub(super) fn is_done(&self) -> bool { - self.time_elapsed >= self.total_length - } - /// Pick a random points inside a ship's collider - fn random_in_ship(&mut self, ship_content: &Ship, collider: &Collider) -> Vector2 { + fn random_in_ship(&mut self, ship: &Ship, collider: &Collider) -> Vector2 { let mut y = 0.0; let mut x = 0.0; let mut a = false; while !a { - y = self.rng.gen_range(-1.0..=1.0) * ship_content.size / 2.0; - x = self.rng.gen_range(-1.0..=1.0) * ship_content.size * ship_content.sprite.aspect - / 2.0; + y = self.rng.gen_range(-1.0..=1.0) * ship.size / 2.0; + x = self.rng.gen_range(-1.0..=1.0) * ship.size * ship.sprite.aspect / 2.0; a = collider.shape().contains_local_point(&point![x, y]); } Vector2 { x, y } @@ -45,24 +41,29 @@ impl ShipCollapseSequence { pub(super) fn step( &mut self, res: &mut StepResources, - ship_handle: ShipHandle, + ship_data: &GxShip, rigid_body: &mut RigidBody, collider: &mut Collider, ) { - let ship_content = res.ct.get_ship(ship_handle); + let ship_content = res.ct.get_ship(ship_data.get_content()); let ship_pos = util::rigidbody_position(rigid_body); let ship_rot = util::rigidbody_rotation(rigid_body); let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 }); + let (total, elapsed) = ship_data.get_state().collapse_state().unwrap(); + // How much time has passed since step() was last called + let delta = elapsed - self.last_call; + // The fraction of this collapse sequence that has been played - let frac_done = self.time_elapsed / self.total_length; + let frac_done = elapsed / total; // Trigger collapse events for event in &ship_content.collapse.events { match event { CollapseEvent::Effect(event) => { - if (event.time > self.time_elapsed && event.time <= self.time_elapsed + res.t) - || (event.time == 0.0 && self.time_elapsed == 0.0) + if (event.time > self.last_call && event.time <= elapsed) + || (event.time == 0.0 && self.last_call == 0.0) + // ^^ Don't miss events scheduled at the very start of the sequence! { for spawner in &event.effects { let effect = res.ct.get_effect(spawner.effect); @@ -112,7 +113,9 @@ impl ShipCollapseSequence { return y; }; - let p_add = (res.t / self.total_length) * pdf(frac_done) * spawner.count; + // Notice that we don't use res.t here, since ship state is updated earlier + // ( through self.data.step() ) + let p_add = (delta / total) * pdf(frac_done) * spawner.count; if self.rng.gen_range(0.0..=1.0) <= p_add { let pos = if let Some(pos) = spawner.pos { @@ -138,6 +141,6 @@ impl ShipCollapseSequence { } } - self.time_elapsed += res.t; + self.last_call = elapsed; } } diff --git a/crates/systemsim/src/objects/ship.rs b/crates/systemsim/src/objects/ship.rs index 70c304d..53e1656 100644 --- a/crates/systemsim/src/objects/ship.rs +++ b/crates/systemsim/src/objects/ship.rs @@ -1,6 +1,6 @@ use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero}; -use galactica_content::{Content, FactionHandle}; -use galactica_galaxy::GxShipHandle; +use galactica_content::{Content, FactionHandle, ShipHandle}; +use galactica_galaxy::ship::{GxShip, ShipPersonality, ShipState}; use nalgebra::{point, vector}; use rand::Rng; use rapier2d::{ @@ -50,46 +50,34 @@ pub struct SySimShip { pub collider: ColliderHandle, /// This ship's game data - pub data_handle: GxShipHandle, + pub data: GxShip, /// This ship's controls pub(crate) controls: ShipControls, /// This ship's collapse sequence - collapse_sequence: ShipCollapseSequence, - - /// This ship's faction. - /// This is technically redundant, faction is also stored in - /// game data, but that's destroyed once the ship dies. - /// We need the faction for the collapse sequence! - faction: FactionHandle, + collapse_sequence: Option, } impl SySimShip { /// Make a new ship pub(crate) fn new( ct: &Content, - data_handle: GxShipHandle, + handle: ShipHandle, + personality: ShipPersonality, faction: FactionHandle, rigid_body: RigidBodyHandle, collider: ColliderHandle, ) -> Self { - let ship_content = ct.get_ship(data_handle.content_handle()); SySimShip { rigid_body, collider, - data_handle, + data: GxShip::new(ct, handle, faction, personality), controls: ShipControls::new(), - faction, - collapse_sequence: ShipCollapseSequence::new(ship_content.collapse.length), + collapse_sequence: Some(ShipCollapseSequence::new()), } } - /// If this is true, remove this ship from the physics system. - pub fn should_be_removed(&self) -> bool { - self.collapse_sequence.is_done() - } - /// Step this ship's state by t seconds pub fn step( &mut self, @@ -97,18 +85,18 @@ impl SySimShip { rigid_body: &mut RigidBody, collider: &mut Collider, ) { - let ship_data = res.gx.get_ship(self.data_handle); - if ship_data.is_none() { - // If ship data is none, it has been removed because the ship has been destroyed. - // play collapse sequence. - self.collapse_sequence.step( - res, - self.data_handle.content_handle(), - rigid_body, - collider, - ); - } else { - return self.step_live(res, rigid_body, collider); + self.data.step(res.t); + match self.data.get_state() { + ShipState::Collapsing { .. } => { + // Borrow checker hack, so we may pass self.data + // to the collapse sequence + let mut seq = self.collapse_sequence.take().unwrap(); + seq.step(res, &self.data, rigid_body, collider); + self.collapse_sequence = Some(seq); + } + ShipState::Flying => { + return self.step_live(res, rigid_body, collider); + } } } @@ -119,14 +107,13 @@ impl SySimShip { rigid_body: &mut RigidBody, collider: &mut Collider, ) { - let ship = res.gx.get_ship(self.data_handle).unwrap(); - let ship_content = res.ct.get_ship(self.data_handle.content_handle()); + let ship_content = res.ct.get_ship(self.data.get_content()); let ship_pos = util::rigidbody_position(&rigid_body); let ship_rot = util::rigidbody_rotation(rigid_body); let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 }); let mut rng = rand::thread_rng(); - if ship.get_hull() <= ship_content.damage.hull { + if self.data.get_hull() <= ship_content.damage.hull { for e in &ship_content.damage.effects { if rng.gen_range(0.0..=1.0) <= res.t / e.frequency { let effect = res.ct.get_effect(e.effect); @@ -171,19 +158,24 @@ impl SySimShip { if self.controls.thrust { rigid_body.apply_impulse( - vector![engine_force.x, engine_force.y] * ship.get_outfits().get_engine_thrust(), + vector![engine_force.x, engine_force.y] + * self.data.get_outfits().get_engine_thrust(), true, ); } if self.controls.right { - rigid_body - .apply_torque_impulse(ship.get_outfits().get_steer_power() * -100.0 * res.t, true); + rigid_body.apply_torque_impulse( + self.data.get_outfits().get_steer_power() * -100.0 * res.t, + true, + ); } if self.controls.left { - rigid_body - .apply_torque_impulse(ship.get_outfits().get_steer_power() * 100.0 * res.t, true); + rigid_body.apply_torque_impulse( + self.data.get_outfits().get_steer_power() * 100.0 * res.t, + true, + ); } } } @@ -193,9 +185,4 @@ impl SySimShip { pub fn get_controls(&self) -> &ShipControls { &self.controls } - - /// Get this ship's faction - pub fn get_faction(&self) -> FactionHandle { - self.faction - } } diff --git a/crates/systemsim/src/stepresources.rs b/crates/systemsim/src/stepresources.rs index fe5dc6a..32a342d 100644 --- a/crates/systemsim/src/stepresources.rs +++ b/crates/systemsim/src/stepresources.rs @@ -1,6 +1,5 @@ use crate::{wrapper::Wrapper, ParticleBuilder}; use galactica_content::Content; -use galactica_galaxy::Galaxy; use galactica_util::timing::Timing; /// External resources we need to compute time steps @@ -8,9 +7,6 @@ pub struct StepResources<'a> { /// Game content pub ct: &'a Content, - /// Game data - pub gx: &'a mut Galaxy, - /// Length of time step pub t: f32, diff --git a/crates/systemsim/src/systemsim.rs b/crates/systemsim/src/systemsim.rs index 2707409..529fbdd 100644 --- a/crates/systemsim/src/systemsim.rs +++ b/crates/systemsim/src/systemsim.rs @@ -1,11 +1,9 @@ use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero}; use galactica_content::{ - Content, GunPoint, OutfitHandle, ProjectileCollider, Relationship, SystemHandle, -}; -use galactica_galaxy::{ - ship::{GxShip, ShipPersonality}, - Galaxy, GxShipHandle, + Content, FactionHandle, GunPoint, OutfitHandle, ProjectileCollider, Relationship, ShipHandle, + SystemHandle, }; +use galactica_galaxy::ship::ShipPersonality; use galactica_playeragent::PlayerAgent; use nalgebra::{point, vector}; use rand::Rng; @@ -22,6 +20,12 @@ use crate::{ util, ParticleBuilder, StepResources, }; +// TODO: replace with a more generic handle +/// A handle for a ship in this simulation +/// This lets other crates reference ships +/// without including `rapier2d`. +pub struct SySimShipHandle(pub ColliderHandle); + /// Manages the physics state of one system #[derive(Clone)] pub struct SystemSim { @@ -32,11 +36,8 @@ pub struct SystemSim { collider_set: ColliderSet, projectiles: HashMap, - ships: HashMap, - - // TODO: these don't need to be cloned each frame - ship_behaviors: HashMap, - collider_ship_table: HashMap, + ships: HashMap, + ship_behaviors: HashMap, } // Private methods @@ -65,26 +66,22 @@ impl<'a> SystemSim { return Some((r, p)); } - fn remove_ship(&mut self, res: &mut StepResources, s: ColliderHandle) { - let s = match self.collider_ship_table.get(&s) { + fn remove_ship(&mut self, res: &mut StepResources, colliderhandle: ColliderHandle) { + let ship = match self.ships.get(&colliderhandle) { None => return, - Some(s) => match self.ships.get(s) { - None => return, - Some(s) => s, - }, + Some(s) => s, }; self.rigid_body_set.remove( - s.rigid_body, + ship.rigid_body, &mut res.wrapper.im, &mut self.collider_set, &mut res.wrapper.ij, &mut res.wrapper.mj, true, ); - let h = self.collider_ship_table.remove(&s.collider).unwrap(); - self.ship_behaviors.remove(&h); - self.ships.remove(&h); + self.ships.remove(&colliderhandle).unwrap(); + self.ship_behaviors.remove(&colliderhandle); } fn collide_projectile_ship( @@ -94,9 +91,7 @@ impl<'a> SystemSim { ship_h: ColliderHandle, ) { let projectile = self.projectiles.get(&projectile_h); - let ship = self - .ships - .get_mut(self.collider_ship_table.get(&ship_h).unwrap()); + let ship = self.ships.get_mut(&ship_h); if projectile.is_none() || ship.is_none() { return; } @@ -104,13 +99,10 @@ impl<'a> SystemSim { let ship = ship.unwrap(); let f = res.ct.get_faction(projectile.faction); - let r = f.relationships.get(&ship.get_faction()).unwrap(); + let r = f.relationships.get(&ship.data.get_faction()).unwrap(); let destory_projectile = match r { Relationship::Hostile => { - // We only apply damage if the target ship is alive - if let Some(ship_d) = res.gx.get_ship_mut(ship.data_handle) { - ship_d.apply_damage(projectile.content.damage); - } + ship.data.apply_damage(res.ct, projectile.content.damage); true } _ => false, @@ -161,38 +153,27 @@ impl<'a> SystemSim { // Public methods impl SystemSim { /// Create a new physics system - pub fn new(ct: &Content, gx: &Galaxy, system: SystemHandle) -> Self { - let mut w = Self { + pub fn new(_ct: &Content, system: SystemHandle) -> Self { + Self { _system: system, rigid_body_set: RigidBodySet::new(), collider_set: ColliderSet::new(), projectiles: HashMap::new(), ships: HashMap::new(), ship_behaviors: HashMap::new(), - collider_ship_table: HashMap::new(), - }; - - // TODO: guarantee not touching - // TODO: add, remove ships each tick - // Maybe store position in galaxy crate? - let mut rng = rand::thread_rng(); - for s in gx.iter_ships() { - w.add_ship( - ct, - s, - Point2 { - x: rng.gen_range(-500.0..=500.0), - y: rng.gen_range(-500.0..=500.0), - }, - ); } - - return w; } /// Add a ship to this physics system - pub fn add_ship(&mut self, ct: &Content, ship: &GxShip, position: Point2) { - let ship_content = ct.get_ship(ship.get_content()); + pub fn add_ship( + &mut self, + ct: &Content, + handle: ShipHandle, + faction: FactionHandle, + personality: ShipPersonality, + position: Point2, + ) -> SySimShipHandle { + let ship_content = ct.get_ship(handle); let cl = ColliderBuilder::convex_decomposition( &ship_content.collision.points[..], &ship_content.collision.indices[..], @@ -209,25 +190,25 @@ impl SystemSim { .translation(vector![position.x, position.y]) .can_sleep(false); - let r = self.rigid_body_set.insert(rb.build()); - let c = self - .collider_set - .insert_with_parent(cl.build(), r, &mut self.rigid_body_set); - - self.collider_ship_table.insert(c, ship.get_handle()); + let ridid_body = self.rigid_body_set.insert(rb.build()); + let collider = + self.collider_set + .insert_with_parent(cl.build(), ridid_body, &mut self.rigid_body_set); self.ship_behaviors.insert( - ship.get_handle(), - match ship.get_personality() { + collider, + match personality { ShipPersonality::Dummy | ShipPersonality::Player => ShipController::new_null(), ShipPersonality::Point => ShipController::new_point(), }, ); self.ships.insert( - ship.get_handle(), - SySimShip::new(ct, ship.get_handle(), ship.get_faction(), r, c), + collider, + SySimShip::new(ct, handle, personality, faction, ridid_body, collider), ); + + return SySimShipHandle(collider); } /// Update a player ship's controls @@ -254,69 +235,59 @@ impl SystemSim { // TODO: don't allocate! let mut projectiles = Vec::new(); let mut to_remove = Vec::new(); - for (_, handle) in &self.collider_ship_table { + // Again, borrow checker hack. TODO: fix + let keys: Vec<_> = self.ships.keys().map(|c| *c).collect(); + for collider in keys { // Borrow immutably for now... // (required to compute controls) - let ship_object = self.ships.get(handle).unwrap(); - if ship_object.should_be_removed() { - to_remove.push(ship_object.collider); + let ship = self.ships.get(&collider).unwrap(); + if ship.data.get_state().should_be_removed() { + to_remove.push(collider); continue; } - // Short-circuit continue if this ship isn't in game data - // (which means it's playing a collapse sequence) - if res.gx.get_ship(*handle).is_none() { - let ship_object = self.ships.get_mut(handle).unwrap(); - ship_object.step( + // This ship is playing a collapse sequence + // and needs no additional logic + if ship.data.get_state().is_collapsing() { + let ship = self.ships.get_mut(&collider).unwrap(); + ship.step( res, - &mut self.rigid_body_set[ship_object.rigid_body], - &mut self.collider_set[ship_object.collider], + &mut self.rigid_body_set[ship.rigid_body], + &mut self.collider_set[ship.collider], ); continue; } // Compute new controls let controls; - let b = self.ship_behaviors.get_mut(handle).unwrap(); - controls = b.update_controls( - &res, - &self.rigid_body_set, - &self.ships, - ship_object.rigid_body, - *handle, - ); + let b = self.ship_behaviors.get_mut(&collider).unwrap(); + controls = b.update_controls(&res, &self.rigid_body_set, &self.ships, ship.collider); + + let ship = self.ships.get_mut(&collider).unwrap(); - // Now re-borrow mutably to apply changes - let ship_object = self.ships.get_mut(handle).unwrap(); if let Some(controls) = controls { - ship_object.controls = controls; + ship.controls = controls; } - ship_object.step( + ship.step( res, - &mut self.rigid_body_set[ship_object.rigid_body], - &mut self.collider_set[ship_object.collider], + &mut self.rigid_body_set[ship.rigid_body], + &mut self.collider_set[ship.collider], ); // If we're firing, try to fire each gun - if ship_object.controls.guns { - let ship_data = res.gx.get_ship_mut(ship_object.data_handle).unwrap(); - + if ship.controls.guns { // TODO: don't allocate here. This is a hack to satisfy the borrow checker, // convert this to a refcell or do the replace dance. - let pairs: Vec<(GunPoint, Option)> = ship_data + let pairs: Vec<(GunPoint, Option)> = ship + .data .get_outfits() .iter_gun_points() .map(|(p, o)| (p.clone(), o.clone())) .collect(); for (gun, outfit) in pairs { - if ship_data.fire_gun(res.ct, &gun) { - projectiles.push(( - ship_object.data_handle, - ship_object.rigid_body, - gun.clone(), - outfit.unwrap(), - )); + if ship.data.fire_gun(res.ct, &gun) { + projectiles.push((ship.collider, gun.clone(), outfit.unwrap())); } } } @@ -328,11 +299,11 @@ impl SystemSim { } // Create projectiles - for (ship_dat, rigid_body, gun_point, outfit) in projectiles { + for (collider, gun_point, outfit) in projectiles { let mut rng = rand::thread_rng(); - let rigid_body = self.get_rigid_body(rigid_body).unwrap(); - let ship_dat = res.gx.get_ship(ship_dat).unwrap(); + let ship = self.ships.get(&collider).unwrap(); + let rigid_body = self.get_rigid_body(ship.rigid_body).unwrap(); let ship_pos = util::rigidbody_position(rigid_body); let ship_rot = util::rigidbody_rotation(rigid_body); let ship_vel = util::rigidbody_velocity(rigid_body); @@ -382,7 +353,7 @@ impl SystemSim { SySimProjectile::new( outfit.projectile.clone(), rigid_body, - ship_dat.get_faction(), + ship.data.get_faction(), collider, ), ); @@ -416,11 +387,7 @@ impl SystemSim { }; let p = self.projectiles.get(&a); - let s = self.ships.get_mut(match self.collider_ship_table.get(&b) { - // If none, this ship is dead. - Some(x) => x, - None => continue, - }); + let s = self.ships.get_mut(&b); if p.is_none() || s.is_none() { continue; } @@ -476,8 +443,13 @@ impl SystemSim { // Public getters impl SystemSim { /// Get a ship physics object - pub fn get_ship(&self, ship: GxShipHandle) -> Option<&SySimShip> { - self.ships.get(&ship) + pub fn get_ship(&self, ship: &SySimShipHandle) -> Option<&SySimShip> { + self.ships.get(&ship.0) + } + + /// Get a ship physics object + pub fn get_ship_mut(&mut self, ship: &SySimShipHandle) -> Option<&mut SySimShip> { + self.ships.get_mut(&ship.0) } /// Get a rigid body from a handle diff --git a/crates/util/src/timing.rs b/crates/util/src/timing.rs index 75b4df2..06c9812 100644 --- a/crates/util/src/timing.rs +++ b/crates/util/src/timing.rs @@ -9,10 +9,6 @@ pub struct Timing { pub frame: Duration, frame_timer: Instant, - /// The time we spent simulating game state - pub galaxy: Duration, - galaxy_timer: Instant, - /// The time we spent simulating physics pub physics_sim: Duration, physics_sim_timer: Instant, @@ -29,10 +25,8 @@ impl Timing { pub fn new() -> Self { Self { frame: Duration::ZERO, - galaxy: Duration::ZERO, physics_sim: Duration::ZERO, physics_ship: Duration::ZERO, - galaxy_timer: Instant::now(), physics_sim_timer: Instant::now(), physics_ship_timer: Instant::now(), frame_timer: Instant::now(), @@ -44,11 +38,6 @@ impl Timing { self.frame_timer = Instant::now(); } - /// Start galaxy timer - pub fn start_galaxy(&mut self) { - self.galaxy_timer = Instant::now(); - } - /// Start physics sim timer pub fn start_physics_sim(&mut self) { self.physics_sim_timer = Instant::now(); @@ -64,11 +53,6 @@ impl Timing { self.frame = self.frame_timer.elapsed(); } - /// Record galaxy simulation time. - pub fn mark_galaxy(&mut self) { - self.galaxy = self.galaxy_timer.elapsed(); - } - /// Record physics simulation time pub fn mark_physics_sim(&mut self) { self.physics_sim = self.physics_sim_timer.elapsed();