Reorganized system simulation
parent
5015040226
commit
e60670c189
|
@ -699,6 +699,7 @@ dependencies = [
|
||||||
"galactica-galaxy",
|
"galactica-galaxy",
|
||||||
"galactica-util",
|
"galactica-util",
|
||||||
"pollster",
|
"pollster",
|
||||||
|
"rapier2d",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
|
@ -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 {
|
content.systems.push(Self {
|
||||||
name: system_name,
|
name: system_name,
|
||||||
objects,
|
objects,
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
use cgmath::Point2;
|
use cgmath::Point2;
|
||||||
use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle};
|
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_playeragent::PlayerAgent;
|
||||||
use galactica_systemsim::{ParticleBuilder, StepResources, SystemSim, Wrapper};
|
use galactica_systemsim::{ParticleBuilder, StepResources, SySimShipHandle, SystemSim, Wrapper};
|
||||||
use galactica_util::timing::Timing;
|
use galactica_util::timing::Timing;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GameState {
|
pub struct GameState {
|
||||||
pub gx: Galaxy,
|
|
||||||
pub systemsim: SystemSim,
|
pub systemsim: SystemSim,
|
||||||
pub timing: Timing,
|
pub timing: Timing,
|
||||||
pub start_instant: Instant,
|
pub start_instant: Instant,
|
||||||
|
@ -37,57 +36,56 @@ pub struct Game {
|
||||||
unsafe impl<'a> Send for Game {}
|
unsafe impl<'a> Send for Game {}
|
||||||
|
|
||||||
impl<'a> Game {
|
impl<'a> Game {
|
||||||
pub fn make_player(&mut self) -> GxShipHandle {
|
pub fn make_player(&mut self) -> SySimShipHandle {
|
||||||
let player = self.state.gx.create_ship(
|
let player = self.state.systemsim.add_ship(
|
||||||
&self.ct,
|
&self.ct,
|
||||||
ShipHandle { index: 0 },
|
ShipHandle { index: 0 },
|
||||||
FactionHandle { index: 0 },
|
FactionHandle { index: 0 },
|
||||||
ShipPersonality::Player,
|
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
|
let s = self.state.systemsim.get_ship_mut(&player).unwrap();
|
||||||
.systemsim
|
s.data
|
||||||
.add_ship(&self.ct, &s, Point2 { x: 0.0, y: 0.0 });
|
.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;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(ct: Content) -> Self {
|
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,
|
&ct,
|
||||||
ShipHandle { index: 0 },
|
ShipHandle { index: 0 },
|
||||||
FactionHandle { index: 1 },
|
FactionHandle { index: 1 },
|
||||||
ShipPersonality::Dummy,
|
ShipPersonality::Point,
|
||||||
&SystemHandle { index: 0 },
|
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,
|
&ct,
|
||||||
ShipHandle { index: 0 },
|
ShipHandle { index: 0 },
|
||||||
FactionHandle { index: 0 },
|
FactionHandle { index: 0 },
|
||||||
ShipPersonality::Point,
|
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 {
|
let state = GameState {
|
||||||
gx,
|
|
||||||
systemsim,
|
systemsim,
|
||||||
timing: Timing::new(),
|
timing: Timing::new(),
|
||||||
start_instant: Instant::now(),
|
start_instant: Instant::now(),
|
||||||
|
@ -121,13 +119,8 @@ impl<'a> Game {
|
||||||
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
|
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
|
||||||
self.new_particles.clear();
|
self.new_particles.clear();
|
||||||
|
|
||||||
self.state.timing.start_galaxy();
|
|
||||||
self.state.gx.step(t);
|
|
||||||
self.state.timing.mark_galaxy();
|
|
||||||
|
|
||||||
self.state.systemsim.step(StepResources {
|
self.state.systemsim.step(StepResources {
|
||||||
ct: &self.ct,
|
ct: &self.ct,
|
||||||
gx: &mut self.state.gx,
|
|
||||||
particles: &mut self.new_particles,
|
particles: &mut self.new_particles,
|
||||||
timing: &mut self.state.timing,
|
timing: &mut self.state.timing,
|
||||||
wrapper: &mut self.wrapper,
|
wrapper: &mut self.wrapper,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use anyhow::{bail, Result};
|
||||||
use galactica_content::{Content, SystemHandle};
|
use galactica_content::{Content, SystemHandle};
|
||||||
use galactica_playeragent::{PlayerAgent, PlayerStatus};
|
use galactica_playeragent::{PlayerAgent, PlayerStatus};
|
||||||
use galactica_render::RenderInput;
|
use galactica_render::RenderInput;
|
||||||
use galactica_systemsim::util;
|
use galactica_systemsim::{util, SySimShipHandle};
|
||||||
use galactica_util::constants::ASSET_CACHE;
|
use galactica_util::constants::ASSET_CACHE;
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
|
@ -41,7 +41,7 @@ fn main() -> Result<()> {
|
||||||
let mut game = game::Game::new(content.clone());
|
let mut game = game::Game::new(content.clone());
|
||||||
let p = game.make_player();
|
let p = game.make_player();
|
||||||
|
|
||||||
let mut player = PlayerAgent::new(p);
|
let mut player = PlayerAgent::new(p.0);
|
||||||
player.set_camera_aspect(
|
player.set_camera_aspect(
|
||||||
gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32,
|
gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32,
|
||||||
);
|
);
|
||||||
|
@ -56,8 +56,7 @@ fn main() -> Result<()> {
|
||||||
ct: &content,
|
ct: &content,
|
||||||
systemsim: &game.get_state().systemsim,
|
systemsim: &game.get_state().systemsim,
|
||||||
particles: game.get_particles(),
|
particles: game.get_particles(),
|
||||||
player_data: player.ship.unwrap(),
|
player_ship: SySimShipHandle(player.ship.unwrap()),
|
||||||
gx: &game.get_state().gx,
|
|
||||||
current_system: SystemHandle { index: 0 },
|
current_system: SystemHandle { index: 0 },
|
||||||
timing: game.get_state().timing.clone(),
|
timing: game.get_state().timing.clone(),
|
||||||
};
|
};
|
||||||
|
@ -77,7 +76,10 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
let player_status = {
|
let player_status = {
|
||||||
let pos = {
|
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 {
|
if let Some(o) = o {
|
||||||
let r = &game.get_state().systemsim.get_rigid_body(o.rigid_body);
|
let r = &game.get_state().systemsim.get_rigid_body(o.rigid_body);
|
||||||
if let Some(r) = r {
|
if let Some(r) = r {
|
||||||
|
|
|
@ -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<GxShipHandle, GxShip>,
|
|
||||||
|
|
||||||
/// Ships indexed by the system they're in.
|
|
||||||
/// A ship must always be in exactly one system.
|
|
||||||
system_ship_table: HashMap<SystemHandle, Vec<GxShipHandle>>,
|
|
||||||
|
|
||||||
/// Systems indexed by which ships they contain.
|
|
||||||
/// A ship must always be in exactly one system.
|
|
||||||
ship_system_table: HashMap<GxShipHandle, SystemHandle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Item = &GxShip> {
|
|
||||||
self.ships.values()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<H: std::hash::Hasher>(&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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 mod ship;
|
||||||
|
|
||||||
pub use galaxy::*;
|
|
||||||
pub use handles::*;
|
|
||||||
|
|
|
@ -1,21 +1,75 @@
|
||||||
use std::{collections::HashMap, time::Instant};
|
use std::{collections::HashMap, time::Instant};
|
||||||
|
|
||||||
use crate::GxShipHandle;
|
|
||||||
|
|
||||||
use super::{OutfitSet, ShipPersonality};
|
use super::{OutfitSet, ShipPersonality};
|
||||||
use galactica_content::{Content, FactionHandle, GunPoint, Outfit, ShipHandle};
|
use galactica_content::{Content, FactionHandle, GunPoint, Outfit, ShipHandle};
|
||||||
use rand::{rngs::ThreadRng, Rng};
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GxShip {
|
pub struct GxShip {
|
||||||
// Metadata values
|
// Metadata values
|
||||||
handle: GxShipHandle,
|
|
||||||
ct_handle: ShipHandle,
|
ct_handle: ShipHandle,
|
||||||
faction: FactionHandle,
|
faction: FactionHandle,
|
||||||
outfits: OutfitSet,
|
outfits: OutfitSet,
|
||||||
|
|
||||||
personality: ShipPersonality,
|
personality: ShipPersonality,
|
||||||
|
|
||||||
|
/// Ship state machine. Keeps track of all possible ship state.
|
||||||
|
/// TODO: document this, draw a graph
|
||||||
|
state: ShipState,
|
||||||
|
|
||||||
// State values
|
// State values
|
||||||
// TODO: unified ship stats struct, like outfit space
|
// TODO: unified ship stats struct, like outfit space
|
||||||
hull: f32,
|
hull: f32,
|
||||||
|
@ -29,16 +83,14 @@ pub struct GxShip {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GxShip {
|
impl GxShip {
|
||||||
pub(crate) fn new(
|
pub fn new(
|
||||||
ct: &Content,
|
ct: &Content,
|
||||||
handle: GxShipHandle,
|
|
||||||
ct_handle: ShipHandle,
|
ct_handle: ShipHandle,
|
||||||
faction: FactionHandle,
|
faction: FactionHandle,
|
||||||
personality: ShipPersonality,
|
personality: ShipPersonality,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let s = ct.get_ship(ct_handle);
|
let s = ct.get_ship(ct_handle);
|
||||||
GxShip {
|
GxShip {
|
||||||
handle,
|
|
||||||
ct_handle,
|
ct_handle,
|
||||||
faction,
|
faction,
|
||||||
outfits: OutfitSet::new(s.space, &s.guns),
|
outfits: OutfitSet::new(s.space, &s.guns),
|
||||||
|
@ -46,6 +98,9 @@ impl GxShip {
|
||||||
last_hit: Instant::now(),
|
last_hit: Instant::now(),
|
||||||
rng: rand::thread_rng(),
|
rng: rand::thread_rng(),
|
||||||
|
|
||||||
|
// TODO: ships must always start landed on planets
|
||||||
|
state: ShipState::Flying,
|
||||||
|
|
||||||
// Initial stats
|
// Initial stats
|
||||||
hull: s.hull,
|
hull: s.hull,
|
||||||
shields: 0.0,
|
shields: 0.0,
|
||||||
|
@ -65,11 +120,6 @@ impl GxShip {
|
||||||
self.outfits.remove(o)
|
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.
|
/// Try to fire a gun.
|
||||||
/// Will panic if `which` isn't a point on this ship.
|
/// Will panic if `which` isn't a point on this ship.
|
||||||
/// Returns `true` if this gun was fired,
|
/// Returns `true` if this gun was fired,
|
||||||
|
@ -93,8 +143,8 @@ impl GxShip {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hit this ship with the given amount of damage
|
/// Hit this ship with the given amount of damage
|
||||||
pub fn apply_damage(&mut self, mut d: f32) {
|
pub fn apply_damage(&mut self, ct: &Content, mut d: f32) {
|
||||||
if self.is_dead() {
|
if self.state.is_collapsing() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.shields >= d {
|
if self.shields >= d {
|
||||||
|
@ -105,26 +155,44 @@ impl GxShip {
|
||||||
self.hull = 0f32.max(self.hull - d);
|
self.hull = 0f32.max(self.hull - d);
|
||||||
}
|
}
|
||||||
self.last_hit = Instant::now();
|
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
|
/// Update this ship's state by `t` seconds
|
||||||
pub fn step(&mut self, t: f32) {
|
pub fn step(&mut self, t: f32) {
|
||||||
// Cooldown guns
|
match self.state {
|
||||||
for (_, c) in &mut self.gun_cooldowns {
|
ShipState::Collapsing {
|
||||||
if *c > 0.0 {
|
ref mut elapsed, ..
|
||||||
*c -= t;
|
} => {
|
||||||
|
*elapsed += t;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Regenerate shields
|
ShipState::Flying => {
|
||||||
let time_since = self.last_hit.elapsed().as_secs_f32();
|
// Cooldown guns
|
||||||
if self.shields != self.outfits.get_shield_strength() {
|
for (_, c) in &mut self.gun_cooldowns {
|
||||||
for g in self.outfits.iter_shield_generators() {
|
if *c > 0.0 {
|
||||||
if time_since >= g.delay {
|
*c -= t;
|
||||||
self.shields += g.generation * t;
|
}
|
||||||
if self.shields > self.outfits.get_shield_strength() {
|
}
|
||||||
self.shields = self.outfits.get_shield_strength();
|
|
||||||
break;
|
// 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
|
// Misc getters, so internal state is untouchable
|
||||||
impl GxShip {
|
impl GxShip {
|
||||||
/// Get a handle to this ship game object
|
/// Get this ship's state
|
||||||
pub fn get_handle(&self) -> GxShipHandle {
|
pub fn get_state(&self) -> &ShipState {
|
||||||
self.handle
|
&self.state
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a handle to this ship's content
|
/// Get a handle to this ship's content
|
||||||
|
|
|
@ -26,3 +26,4 @@ wgpu = { workspace = true }
|
||||||
pollster = { workspace = true }
|
pollster = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
cgmath = { workspace = true }
|
cgmath = { workspace = true }
|
||||||
|
rapier2d = { workspace = true }
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use galactica_content::Content;
|
use galactica_content::Content;
|
||||||
use galactica_galaxy::GxShipHandle;
|
use rapier2d::geometry::ColliderHandle;
|
||||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||||
|
|
||||||
use crate::{camera::Camera, inputstatus::InputStatus, PlayerStatus};
|
use crate::{camera::Camera, inputstatus::InputStatus, PlayerStatus};
|
||||||
|
|
||||||
pub struct PlayerAgent {
|
pub struct PlayerAgent {
|
||||||
/// Which ship this player is controlling
|
/// Which ship this player is controlling
|
||||||
pub ship: Option<GxShipHandle>,
|
pub ship: Option<ColliderHandle>,
|
||||||
|
|
||||||
/// This player's camera
|
/// This player's camera
|
||||||
pub camera: Camera,
|
pub camera: Camera,
|
||||||
|
@ -19,7 +19,7 @@ pub struct PlayerAgent {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerAgent {
|
impl PlayerAgent {
|
||||||
pub fn new(ship: GxShipHandle) -> Self {
|
pub fn new(ship: ColliderHandle) -> Self {
|
||||||
Self {
|
Self {
|
||||||
input: InputStatus::new(),
|
input: InputStatus::new(),
|
||||||
camera: Camera::new(),
|
camera: Camera::new(),
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use cgmath::Point2;
|
use cgmath::Point2;
|
||||||
use galactica_content::{Content, SystemHandle};
|
use galactica_content::{Content, SystemHandle};
|
||||||
use galactica_galaxy::{Galaxy, GxShipHandle};
|
use galactica_systemsim::{ParticleBuilder, SySimShipHandle, SystemSim};
|
||||||
use galactica_systemsim::{ParticleBuilder, SystemSim};
|
|
||||||
use galactica_util::timing::Timing;
|
use galactica_util::timing::Timing;
|
||||||
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -16,7 +15,7 @@ pub struct RenderInput<'a> {
|
||||||
pub camera_pos: Point2<f32>,
|
pub camera_pos: Point2<f32>,
|
||||||
|
|
||||||
/// Player ship data
|
/// Player ship data
|
||||||
pub player_data: GxShipHandle,
|
pub player_ship: SySimShipHandle,
|
||||||
|
|
||||||
/// The system we're currently in
|
/// The system we're currently in
|
||||||
pub current_system: SystemHandle,
|
pub current_system: SystemHandle,
|
||||||
|
@ -34,9 +33,6 @@ pub struct RenderInput<'a> {
|
||||||
/// Game content
|
/// Game content
|
||||||
pub ct: &'a Content,
|
pub ct: &'a Content,
|
||||||
|
|
||||||
/// Game data
|
|
||||||
pub gx: &'a Galaxy,
|
|
||||||
|
|
||||||
/// Particles to spawn during this frame
|
/// Particles to spawn during this frame
|
||||||
pub particles: &'a Vec<ParticleBuilder>,
|
pub particles: &'a Vec<ParticleBuilder>,
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,12 @@ impl GPUState {
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
) {
|
) {
|
||||||
for s in state.systemsim.iter_ships() {
|
for ship in state.systemsim.iter_ships() {
|
||||||
let r = state.systemsim.get_rigid_body(s.rigid_body).unwrap();
|
let r = state.systemsim.get_rigid_body(ship.rigid_body).unwrap();
|
||||||
let ship_pos = util::rigidbody_position(&r);
|
let ship_pos = util::rigidbody_position(&r);
|
||||||
let ship_rot = util::rigidbody_rotation(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_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
|
// Position adjusted for parallax
|
||||||
// TODO: adjust parallax for zoom?
|
// TODO: adjust parallax for zoom?
|
||||||
|
@ -80,16 +80,8 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
self.state.vertex_buffers.object_counter += 1;
|
self.state.vertex_buffers.object_counter += 1;
|
||||||
|
|
||||||
// This will be None if this ship is dead.
|
let flare = ship.data.get_outfits().get_flare_sprite(state.ct);
|
||||||
// (physics object stays around to complete the death animation)
|
if ship.get_controls().thrust && flare.is_some() && ship.data.get_state().is_flying() {
|
||||||
// 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() {
|
|
||||||
for engine_point in &ship_cnt.engines {
|
for engine_point in &ship_cnt.engines {
|
||||||
self.state.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.state.global_uniform.object_buffer,
|
&self.state.global_uniform.object_buffer,
|
||||||
|
|
|
@ -36,9 +36,9 @@ impl FpsIndicator {
|
||||||
self.buffer.set_text(
|
self.buffer.set_text(
|
||||||
&mut state.text_font_system,
|
&mut state.text_font_system,
|
||||||
&format!(
|
&format!(
|
||||||
"Frame: {:#?} ({:05.00})\nGame: {:05.02}%\nShips: {:05.02}%\nPhys: {:05.02}%\n",
|
"Frame: {:#?} ({:05.00})\nShips: {:05.02}%\nPhys: {:05.02}%\n",
|
||||||
input.timing.frame, 1.0 /input.timing.frame.as_secs_f32(),
|
input.timing.frame,
|
||||||
100.0 * (input.timing.galaxy.as_secs_f32() / input.timing.frame.as_secs_f32()),
|
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_sim.as_secs_f32() / input.timing.frame.as_secs_f32()),
|
||||||
100.0
|
100.0
|
||||||
* (input.timing.physics_ship.as_secs_f32() / input.timing.frame.as_secs_f32()),
|
* (input.timing.physics_ship.as_secs_f32() / input.timing.frame.as_secs_f32()),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use cgmath::{Deg, InnerSpace, Point2, Rad, Vector2};
|
use cgmath::{Deg, InnerSpace, Point2, Rad, Vector2};
|
||||||
|
use galactica_galaxy::ship::ShipState;
|
||||||
use galactica_systemsim::util;
|
use galactica_systemsim::util;
|
||||||
use galactica_util::constants::UI_SPRITE_INSTANCE_LIMIT;
|
use galactica_util::constants::UI_SPRITE_INSTANCE_LIMIT;
|
||||||
|
|
||||||
|
@ -31,13 +32,17 @@ impl Radar {
|
||||||
|
|
||||||
// TODO: maybe a cleaner solution for last posititon?
|
// TODO: maybe a cleaner solution for last posititon?
|
||||||
// This is necessary because the player may be dead or landed
|
// 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_ship = input.systemsim.get_ship(&input.player_ship).unwrap();
|
||||||
let player_body = input
|
|
||||||
.systemsim
|
|
||||||
.get_rigid_body(player_world_object.rigid_body)
|
|
||||||
.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");
|
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() {
|
for (s, r) in input.systemsim.iter_ship_body() {
|
||||||
// This will be None if this ship is dead.
|
// This will be None if this ship is dead.
|
||||||
// Stays around while the physics system runs a collapse sequence
|
// Stays around while the physics system runs a collapse sequence
|
||||||
let color = match input.gx.get_ship(s.data_handle) {
|
let color = match s.data.get_state() {
|
||||||
None => {
|
ShipState::Collapsing { .. } => {
|
||||||
// TODO: configurable
|
// TODO: configurable
|
||||||
[0.2, 0.2, 0.2, 1.0]
|
[0.2, 0.2, 0.2, 1.0]
|
||||||
}
|
}
|
||||||
Some(data) => {
|
ShipState::Flying => {
|
||||||
let c = input.ct.get_faction(data.get_faction()).color;
|
let c = input.ct.get_faction(s.data.get_faction()).color;
|
||||||
[c[0], c[1], c[2], 1.0]
|
[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 size = (ship.size * ship.sprite.aspect) * ship_scale;
|
||||||
let p = util::rigidbody_position(r);
|
let p = util::rigidbody_position(r);
|
||||||
let d = (p - self.last_player_position) / radar_range;
|
let d = (p - self.last_player_position) / radar_range;
|
||||||
|
|
|
@ -29,19 +29,17 @@ impl Status {
|
||||||
let current_shields;
|
let current_shields;
|
||||||
let current_hull;
|
let current_hull;
|
||||||
let max_hull;
|
let max_hull;
|
||||||
if let Some(player_world_object) = input.systemsim.get_ship(input.player_data) {
|
let player_ship = input.systemsim.get_ship(&input.player_ship).unwrap();
|
||||||
let data = input.gx.get_ship(player_world_object.data_handle).unwrap();
|
|
||||||
max_shields = data.get_outfits().get_shield_strength();
|
match player_ship.data.get_state() {
|
||||||
current_shields = data.get_shields();
|
galactica_galaxy::ship::ShipState::Collapsing { .. }
|
||||||
current_hull = data.get_hull();
|
| galactica_galaxy::ship::ShipState::Flying => {
|
||||||
max_hull = input.ct.get_ship(data.get_content()).hull;
|
max_shields = player_ship.data.get_outfits().get_shield_strength();
|
||||||
} else {
|
current_shields = player_ship.data.get_shields();
|
||||||
// Sensible defaults if ship doesn't exist (landed or dead)
|
current_hull = player_ship.data.get_hull();
|
||||||
max_shields = 1.0;
|
max_hull = input.ct.get_ship(player_ship.data.get_content()).hull;
|
||||||
current_shields = 0.0;
|
}
|
||||||
current_hull = 0.0;
|
}
|
||||||
max_hull = 1.0;
|
|
||||||
};
|
|
||||||
|
|
||||||
state.queue.write_buffer(
|
state.queue.write_buffer(
|
||||||
&state.vertex_buffers.ui.instances,
|
&state.vertex_buffers.ui.instances,
|
||||||
|
|
|
@ -6,8 +6,7 @@ mod point;
|
||||||
use null::*;
|
use null::*;
|
||||||
use point::PointShipController;
|
use point::PointShipController;
|
||||||
|
|
||||||
use galactica_galaxy::GxShipHandle;
|
use rapier2d::{dynamics::RigidBodySet, geometry::ColliderHandle};
|
||||||
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
|
|
||||||
use std::{collections::HashMap, fmt::Debug};
|
use std::{collections::HashMap, fmt::Debug};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -41,13 +40,12 @@ impl ShipController {
|
||||||
&mut self,
|
&mut self,
|
||||||
res: &StepResources,
|
res: &StepResources,
|
||||||
rigid_bodies: &RigidBodySet,
|
rigid_bodies: &RigidBodySet,
|
||||||
ships: &HashMap<GxShipHandle, SySimShip>,
|
ships: &HashMap<ColliderHandle, SySimShip>,
|
||||||
this_ship: RigidBodyHandle,
|
this_ship: ColliderHandle,
|
||||||
this_data: GxShipHandle,
|
|
||||||
) -> Option<ShipControls> {
|
) -> Option<ShipControls> {
|
||||||
match self {
|
match self {
|
||||||
Self::Null(n) => n.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, this_data),
|
Self::Point(p) => p.update_controls(res, rigid_bodies, ships, this_ship),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,8 +63,7 @@ where
|
||||||
&mut self,
|
&mut self,
|
||||||
res: &StepResources,
|
res: &StepResources,
|
||||||
rigid_bodies: &RigidBodySet,
|
rigid_bodies: &RigidBodySet,
|
||||||
ships: &HashMap<GxShipHandle, SySimShip>,
|
ships: &HashMap<ColliderHandle, SySimShip>,
|
||||||
this_ship: RigidBodyHandle,
|
this_ship: ColliderHandle,
|
||||||
this_data: GxShipHandle,
|
|
||||||
) -> Option<ShipControls>;
|
) -> Option<ShipControls>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use galactica_galaxy::GxShipHandle;
|
use rapier2d::{dynamics::RigidBodySet, geometry::ColliderHandle};
|
||||||
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::ShipControllerStruct;
|
use super::ShipControllerStruct;
|
||||||
|
@ -25,9 +24,8 @@ impl ShipControllerStruct for NullShipController {
|
||||||
&mut self,
|
&mut self,
|
||||||
_res: &StepResources,
|
_res: &StepResources,
|
||||||
_rigid_bodies: &RigidBodySet,
|
_rigid_bodies: &RigidBodySet,
|
||||||
_ships: &HashMap<GxShipHandle, SySimShip>,
|
_ships: &HashMap<ColliderHandle, SySimShip>,
|
||||||
_this_ship: RigidBodyHandle,
|
_this_ship: ColliderHandle,
|
||||||
_this_data: GxShipHandle,
|
|
||||||
) -> Option<ShipControls> {
|
) -> Option<ShipControls> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use cgmath::{Deg, InnerSpace};
|
use cgmath::{Deg, InnerSpace};
|
||||||
use galactica_content::Relationship;
|
use galactica_content::Relationship;
|
||||||
use galactica_galaxy::GxShipHandle;
|
use rapier2d::{dynamics::RigidBodySet, geometry::ColliderHandle};
|
||||||
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::ShipControllerStruct;
|
use super::ShipControllerStruct;
|
||||||
|
@ -27,35 +26,28 @@ impl ShipControllerStruct for PointShipController {
|
||||||
&mut self,
|
&mut self,
|
||||||
res: &StepResources,
|
res: &StepResources,
|
||||||
rigid_bodies: &RigidBodySet,
|
rigid_bodies: &RigidBodySet,
|
||||||
ships: &HashMap<GxShipHandle, SySimShip>,
|
ships: &HashMap<ColliderHandle, SySimShip>,
|
||||||
this_ship: RigidBodyHandle,
|
this_ship: ColliderHandle,
|
||||||
this_data: GxShipHandle,
|
|
||||||
) -> Option<ShipControls> {
|
) -> Option<ShipControls> {
|
||||||
let mut controls = ShipControls::new();
|
let mut controls = ShipControls::new();
|
||||||
|
|
||||||
let this_rigidbody = rigid_bodies.get(this_ship).unwrap();
|
let my_ship = ships.get(&this_ship).unwrap();
|
||||||
let my_data = res.gx.get_ship(this_data).unwrap();
|
let this_rigidbody = rigid_bodies.get(my_ship.rigid_body).unwrap();
|
||||||
let my_position = util::rigidbody_position(this_rigidbody);
|
let my_position = util::rigidbody_position(this_rigidbody);
|
||||||
let my_rotation = util::rigidbody_rotation(this_rigidbody);
|
let my_rotation = util::rigidbody_rotation(this_rigidbody);
|
||||||
let my_angvel = this_rigidbody.angvel();
|
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
|
// Iterate all possible targets
|
||||||
let mut hostile_ships = ships
|
let mut hostile_ships = ships
|
||||||
.values()
|
.values()
|
||||||
.filter(|s| {
|
.filter(
|
||||||
let data = res.gx.get_ship(s.data_handle);
|
// Target only flying ships we're hostile towards
|
||||||
if let Some(data) = data {
|
|s| match my_faction.relationships.get(&s.data.get_faction()).unwrap() {
|
||||||
match my_faction.relationships.get(&data.get_faction()).unwrap() {
|
Relationship::Hostile => s.data.get_state().is_flying(),
|
||||||
Relationship::Hostile => true,
|
_ => false,
|
||||||
_ => 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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(|s| rigid_bodies.get(s.rigid_body).unwrap());
|
.map(|s| rigid_bodies.get(s.rigid_body).unwrap());
|
||||||
|
|
||||||
// Find the closest target
|
// Find the closest target
|
||||||
|
|
|
@ -12,5 +12,5 @@ mod wrapper;
|
||||||
|
|
||||||
pub use particlebuilder::*;
|
pub use particlebuilder::*;
|
||||||
pub use stepresources::*;
|
pub use stepresources::*;
|
||||||
pub use systemsim::SystemSim;
|
pub use systemsim::{SySimShipHandle, SystemSim};
|
||||||
pub use wrapper::Wrapper;
|
pub use wrapper::Wrapper;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
|
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 nalgebra::point;
|
||||||
use rand::{rngs::ThreadRng, Rng};
|
use rand::{rngs::ThreadRng, Rng};
|
||||||
use rapier2d::{dynamics::RigidBody, geometry::Collider};
|
use rapier2d::{dynamics::RigidBody, geometry::Collider};
|
||||||
|
@ -8,34 +9,29 @@ use crate::{util, ParticleBuilder, StepResources};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct ShipCollapseSequence {
|
pub(super) struct ShipCollapseSequence {
|
||||||
total_length: f32,
|
|
||||||
time_elapsed: f32,
|
|
||||||
rng: ThreadRng,
|
rng: ThreadRng,
|
||||||
|
|
||||||
|
/// The elapsed collapse duration when step()
|
||||||
|
/// was last called
|
||||||
|
last_call: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShipCollapseSequence {
|
impl ShipCollapseSequence {
|
||||||
pub(super) fn new(total_length: f32) -> Self {
|
pub(super) fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
total_length,
|
|
||||||
time_elapsed: 0.0,
|
|
||||||
rng: rand::thread_rng(),
|
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
|
/// Pick a random points inside a ship's collider
|
||||||
fn random_in_ship(&mut self, ship_content: &Ship, collider: &Collider) -> Vector2<f32> {
|
fn random_in_ship(&mut self, ship: &Ship, collider: &Collider) -> Vector2<f32> {
|
||||||
let mut y = 0.0;
|
let mut y = 0.0;
|
||||||
let mut x = 0.0;
|
let mut x = 0.0;
|
||||||
let mut a = false;
|
let mut a = false;
|
||||||
while !a {
|
while !a {
|
||||||
y = self.rng.gen_range(-1.0..=1.0) * ship_content.size / 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_content.size * ship_content.sprite.aspect
|
x = self.rng.gen_range(-1.0..=1.0) * ship.size * ship.sprite.aspect / 2.0;
|
||||||
/ 2.0;
|
|
||||||
a = collider.shape().contains_local_point(&point![x, y]);
|
a = collider.shape().contains_local_point(&point![x, y]);
|
||||||
}
|
}
|
||||||
Vector2 { x, y }
|
Vector2 { x, y }
|
||||||
|
@ -45,24 +41,29 @@ impl ShipCollapseSequence {
|
||||||
pub(super) fn step(
|
pub(super) fn step(
|
||||||
&mut self,
|
&mut self,
|
||||||
res: &mut StepResources,
|
res: &mut StepResources,
|
||||||
ship_handle: ShipHandle,
|
ship_data: &GxShip,
|
||||||
rigid_body: &mut RigidBody,
|
rigid_body: &mut RigidBody,
|
||||||
collider: &mut Collider,
|
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_pos = util::rigidbody_position(rigid_body);
|
||||||
let ship_rot = util::rigidbody_rotation(rigid_body);
|
let ship_rot = util::rigidbody_rotation(rigid_body);
|
||||||
let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 });
|
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
|
// 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
|
// Trigger collapse events
|
||||||
for event in &ship_content.collapse.events {
|
for event in &ship_content.collapse.events {
|
||||||
match event {
|
match event {
|
||||||
CollapseEvent::Effect(event) => {
|
CollapseEvent::Effect(event) => {
|
||||||
if (event.time > self.time_elapsed && event.time <= self.time_elapsed + res.t)
|
if (event.time > self.last_call && event.time <= elapsed)
|
||||||
|| (event.time == 0.0 && self.time_elapsed == 0.0)
|
|| (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 {
|
for spawner in &event.effects {
|
||||||
let effect = res.ct.get_effect(spawner.effect);
|
let effect = res.ct.get_effect(spawner.effect);
|
||||||
|
@ -112,7 +113,9 @@ impl ShipCollapseSequence {
|
||||||
return y;
|
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 {
|
if self.rng.gen_range(0.0..=1.0) <= p_add {
|
||||||
let pos = if let Some(pos) = spawner.pos {
|
let pos = if let Some(pos) = spawner.pos {
|
||||||
|
@ -138,6 +141,6 @@ impl ShipCollapseSequence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.time_elapsed += res.t;
|
self.last_call = elapsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
|
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
|
||||||
use galactica_content::{Content, FactionHandle};
|
use galactica_content::{Content, FactionHandle, ShipHandle};
|
||||||
use galactica_galaxy::GxShipHandle;
|
use galactica_galaxy::ship::{GxShip, ShipPersonality, ShipState};
|
||||||
use nalgebra::{point, vector};
|
use nalgebra::{point, vector};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rapier2d::{
|
use rapier2d::{
|
||||||
|
@ -50,46 +50,34 @@ pub struct SySimShip {
|
||||||
pub collider: ColliderHandle,
|
pub collider: ColliderHandle,
|
||||||
|
|
||||||
/// This ship's game data
|
/// This ship's game data
|
||||||
pub data_handle: GxShipHandle,
|
pub data: GxShip,
|
||||||
|
|
||||||
/// This ship's controls
|
/// This ship's controls
|
||||||
pub(crate) controls: ShipControls,
|
pub(crate) controls: ShipControls,
|
||||||
|
|
||||||
/// This ship's collapse sequence
|
/// This ship's collapse sequence
|
||||||
collapse_sequence: ShipCollapseSequence,
|
collapse_sequence: Option<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,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SySimShip {
|
impl SySimShip {
|
||||||
/// Make a new ship
|
/// Make a new ship
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
ct: &Content,
|
ct: &Content,
|
||||||
data_handle: GxShipHandle,
|
handle: ShipHandle,
|
||||||
|
personality: ShipPersonality,
|
||||||
faction: FactionHandle,
|
faction: FactionHandle,
|
||||||
rigid_body: RigidBodyHandle,
|
rigid_body: RigidBodyHandle,
|
||||||
collider: ColliderHandle,
|
collider: ColliderHandle,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let ship_content = ct.get_ship(data_handle.content_handle());
|
|
||||||
SySimShip {
|
SySimShip {
|
||||||
rigid_body,
|
rigid_body,
|
||||||
collider,
|
collider,
|
||||||
data_handle,
|
data: GxShip::new(ct, handle, faction, personality),
|
||||||
controls: ShipControls::new(),
|
controls: ShipControls::new(),
|
||||||
faction,
|
collapse_sequence: Some(ShipCollapseSequence::new()),
|
||||||
collapse_sequence: ShipCollapseSequence::new(ship_content.collapse.length),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
/// Step this ship's state by t seconds
|
||||||
pub fn step(
|
pub fn step(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -97,18 +85,18 @@ impl SySimShip {
|
||||||
rigid_body: &mut RigidBody,
|
rigid_body: &mut RigidBody,
|
||||||
collider: &mut Collider,
|
collider: &mut Collider,
|
||||||
) {
|
) {
|
||||||
let ship_data = res.gx.get_ship(self.data_handle);
|
self.data.step(res.t);
|
||||||
if ship_data.is_none() {
|
match self.data.get_state() {
|
||||||
// If ship data is none, it has been removed because the ship has been destroyed.
|
ShipState::Collapsing { .. } => {
|
||||||
// play collapse sequence.
|
// Borrow checker hack, so we may pass self.data
|
||||||
self.collapse_sequence.step(
|
// to the collapse sequence
|
||||||
res,
|
let mut seq = self.collapse_sequence.take().unwrap();
|
||||||
self.data_handle.content_handle(),
|
seq.step(res, &self.data, rigid_body, collider);
|
||||||
rigid_body,
|
self.collapse_sequence = Some(seq);
|
||||||
collider,
|
}
|
||||||
);
|
ShipState::Flying => {
|
||||||
} else {
|
return self.step_live(res, rigid_body, collider);
|
||||||
return self.step_live(res, rigid_body, collider);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,14 +107,13 @@ impl SySimShip {
|
||||||
rigid_body: &mut RigidBody,
|
rigid_body: &mut RigidBody,
|
||||||
collider: &mut Collider,
|
collider: &mut Collider,
|
||||||
) {
|
) {
|
||||||
let ship = res.gx.get_ship(self.data_handle).unwrap();
|
let ship_content = res.ct.get_ship(self.data.get_content());
|
||||||
let ship_content = res.ct.get_ship(self.data_handle.content_handle());
|
|
||||||
let ship_pos = util::rigidbody_position(&rigid_body);
|
let ship_pos = util::rigidbody_position(&rigid_body);
|
||||||
let ship_rot = util::rigidbody_rotation(rigid_body);
|
let ship_rot = util::rigidbody_rotation(rigid_body);
|
||||||
let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 });
|
let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 });
|
||||||
let mut rng = rand::thread_rng();
|
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 {
|
for e in &ship_content.damage.effects {
|
||||||
if rng.gen_range(0.0..=1.0) <= res.t / e.frequency {
|
if rng.gen_range(0.0..=1.0) <= res.t / e.frequency {
|
||||||
let effect = res.ct.get_effect(e.effect);
|
let effect = res.ct.get_effect(e.effect);
|
||||||
|
@ -171,19 +158,24 @@ impl SySimShip {
|
||||||
|
|
||||||
if self.controls.thrust {
|
if self.controls.thrust {
|
||||||
rigid_body.apply_impulse(
|
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,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.controls.right {
|
if self.controls.right {
|
||||||
rigid_body
|
rigid_body.apply_torque_impulse(
|
||||||
.apply_torque_impulse(ship.get_outfits().get_steer_power() * -100.0 * res.t, true);
|
self.data.get_outfits().get_steer_power() * -100.0 * res.t,
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.controls.left {
|
if self.controls.left {
|
||||||
rigid_body
|
rigid_body.apply_torque_impulse(
|
||||||
.apply_torque_impulse(ship.get_outfits().get_steer_power() * 100.0 * res.t, true);
|
self.data.get_outfits().get_steer_power() * 100.0 * res.t,
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,9 +185,4 @@ impl SySimShip {
|
||||||
pub fn get_controls(&self) -> &ShipControls {
|
pub fn get_controls(&self) -> &ShipControls {
|
||||||
&self.controls
|
&self.controls
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get this ship's faction
|
|
||||||
pub fn get_faction(&self) -> FactionHandle {
|
|
||||||
self.faction
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{wrapper::Wrapper, ParticleBuilder};
|
use crate::{wrapper::Wrapper, ParticleBuilder};
|
||||||
use galactica_content::Content;
|
use galactica_content::Content;
|
||||||
use galactica_galaxy::Galaxy;
|
|
||||||
use galactica_util::timing::Timing;
|
use galactica_util::timing::Timing;
|
||||||
|
|
||||||
/// External resources we need to compute time steps
|
/// External resources we need to compute time steps
|
||||||
|
@ -8,9 +7,6 @@ pub struct StepResources<'a> {
|
||||||
/// Game content
|
/// Game content
|
||||||
pub ct: &'a Content,
|
pub ct: &'a Content,
|
||||||
|
|
||||||
/// Game data
|
|
||||||
pub gx: &'a mut Galaxy,
|
|
||||||
|
|
||||||
/// Length of time step
|
/// Length of time step
|
||||||
pub t: f32,
|
pub t: f32,
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero};
|
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero};
|
||||||
use galactica_content::{
|
use galactica_content::{
|
||||||
Content, GunPoint, OutfitHandle, ProjectileCollider, Relationship, SystemHandle,
|
Content, FactionHandle, GunPoint, OutfitHandle, ProjectileCollider, Relationship, ShipHandle,
|
||||||
};
|
SystemHandle,
|
||||||
use galactica_galaxy::{
|
|
||||||
ship::{GxShip, ShipPersonality},
|
|
||||||
Galaxy, GxShipHandle,
|
|
||||||
};
|
};
|
||||||
|
use galactica_galaxy::ship::ShipPersonality;
|
||||||
use galactica_playeragent::PlayerAgent;
|
use galactica_playeragent::PlayerAgent;
|
||||||
use nalgebra::{point, vector};
|
use nalgebra::{point, vector};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
@ -22,6 +20,12 @@ use crate::{
|
||||||
util, ParticleBuilder, StepResources,
|
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
|
/// Manages the physics state of one system
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SystemSim {
|
pub struct SystemSim {
|
||||||
|
@ -32,11 +36,8 @@ pub struct SystemSim {
|
||||||
collider_set: ColliderSet,
|
collider_set: ColliderSet,
|
||||||
|
|
||||||
projectiles: HashMap<ColliderHandle, SySimProjectile>,
|
projectiles: HashMap<ColliderHandle, SySimProjectile>,
|
||||||
ships: HashMap<GxShipHandle, SySimShip>,
|
ships: HashMap<ColliderHandle, SySimShip>,
|
||||||
|
ship_behaviors: HashMap<ColliderHandle, ShipController>,
|
||||||
// TODO: these don't need to be cloned each frame
|
|
||||||
ship_behaviors: HashMap<GxShipHandle, ShipController>,
|
|
||||||
collider_ship_table: HashMap<ColliderHandle, GxShipHandle>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private methods
|
// Private methods
|
||||||
|
@ -65,26 +66,22 @@ impl<'a> SystemSim {
|
||||||
return Some((r, p));
|
return Some((r, p));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_ship(&mut self, res: &mut StepResources, s: ColliderHandle) {
|
fn remove_ship(&mut self, res: &mut StepResources, colliderhandle: ColliderHandle) {
|
||||||
let s = match self.collider_ship_table.get(&s) {
|
let ship = match self.ships.get(&colliderhandle) {
|
||||||
None => return,
|
None => return,
|
||||||
Some(s) => match self.ships.get(s) {
|
Some(s) => s,
|
||||||
None => return,
|
|
||||||
Some(s) => s,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.rigid_body_set.remove(
|
self.rigid_body_set.remove(
|
||||||
s.rigid_body,
|
ship.rigid_body,
|
||||||
&mut res.wrapper.im,
|
&mut res.wrapper.im,
|
||||||
&mut self.collider_set,
|
&mut self.collider_set,
|
||||||
&mut res.wrapper.ij,
|
&mut res.wrapper.ij,
|
||||||
&mut res.wrapper.mj,
|
&mut res.wrapper.mj,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
let h = self.collider_ship_table.remove(&s.collider).unwrap();
|
self.ships.remove(&colliderhandle).unwrap();
|
||||||
self.ship_behaviors.remove(&h);
|
self.ship_behaviors.remove(&colliderhandle);
|
||||||
self.ships.remove(&h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collide_projectile_ship(
|
fn collide_projectile_ship(
|
||||||
|
@ -94,9 +91,7 @@ impl<'a> SystemSim {
|
||||||
ship_h: ColliderHandle,
|
ship_h: ColliderHandle,
|
||||||
) {
|
) {
|
||||||
let projectile = self.projectiles.get(&projectile_h);
|
let projectile = self.projectiles.get(&projectile_h);
|
||||||
let ship = self
|
let ship = self.ships.get_mut(&ship_h);
|
||||||
.ships
|
|
||||||
.get_mut(self.collider_ship_table.get(&ship_h).unwrap());
|
|
||||||
if projectile.is_none() || ship.is_none() {
|
if projectile.is_none() || ship.is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -104,13 +99,10 @@ impl<'a> SystemSim {
|
||||||
let ship = ship.unwrap();
|
let ship = ship.unwrap();
|
||||||
|
|
||||||
let f = res.ct.get_faction(projectile.faction);
|
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 {
|
let destory_projectile = match r {
|
||||||
Relationship::Hostile => {
|
Relationship::Hostile => {
|
||||||
// We only apply damage if the target ship is alive
|
ship.data.apply_damage(res.ct, projectile.content.damage);
|
||||||
if let Some(ship_d) = res.gx.get_ship_mut(ship.data_handle) {
|
|
||||||
ship_d.apply_damage(projectile.content.damage);
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -161,38 +153,27 @@ impl<'a> SystemSim {
|
||||||
// Public methods
|
// Public methods
|
||||||
impl SystemSim {
|
impl SystemSim {
|
||||||
/// Create a new physics system
|
/// Create a new physics system
|
||||||
pub fn new(ct: &Content, gx: &Galaxy, system: SystemHandle) -> Self {
|
pub fn new(_ct: &Content, system: SystemHandle) -> Self {
|
||||||
let mut w = Self {
|
Self {
|
||||||
_system: system,
|
_system: system,
|
||||||
rigid_body_set: RigidBodySet::new(),
|
rigid_body_set: RigidBodySet::new(),
|
||||||
collider_set: ColliderSet::new(),
|
collider_set: ColliderSet::new(),
|
||||||
projectiles: HashMap::new(),
|
projectiles: HashMap::new(),
|
||||||
ships: HashMap::new(),
|
ships: HashMap::new(),
|
||||||
ship_behaviors: 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
|
/// Add a ship to this physics system
|
||||||
pub fn add_ship(&mut self, ct: &Content, ship: &GxShip, position: Point2<f32>) {
|
pub fn add_ship(
|
||||||
let ship_content = ct.get_ship(ship.get_content());
|
&mut self,
|
||||||
|
ct: &Content,
|
||||||
|
handle: ShipHandle,
|
||||||
|
faction: FactionHandle,
|
||||||
|
personality: ShipPersonality,
|
||||||
|
position: Point2<f32>,
|
||||||
|
) -> SySimShipHandle {
|
||||||
|
let ship_content = ct.get_ship(handle);
|
||||||
let cl = ColliderBuilder::convex_decomposition(
|
let cl = ColliderBuilder::convex_decomposition(
|
||||||
&ship_content.collision.points[..],
|
&ship_content.collision.points[..],
|
||||||
&ship_content.collision.indices[..],
|
&ship_content.collision.indices[..],
|
||||||
|
@ -209,25 +190,25 @@ impl SystemSim {
|
||||||
.translation(vector![position.x, position.y])
|
.translation(vector![position.x, position.y])
|
||||||
.can_sleep(false);
|
.can_sleep(false);
|
||||||
|
|
||||||
let r = self.rigid_body_set.insert(rb.build());
|
let ridid_body = self.rigid_body_set.insert(rb.build());
|
||||||
let c = self
|
let collider =
|
||||||
.collider_set
|
self.collider_set
|
||||||
.insert_with_parent(cl.build(), r, &mut self.rigid_body_set);
|
.insert_with_parent(cl.build(), ridid_body, &mut self.rigid_body_set);
|
||||||
|
|
||||||
self.collider_ship_table.insert(c, ship.get_handle());
|
|
||||||
|
|
||||||
self.ship_behaviors.insert(
|
self.ship_behaviors.insert(
|
||||||
ship.get_handle(),
|
collider,
|
||||||
match ship.get_personality() {
|
match personality {
|
||||||
ShipPersonality::Dummy | ShipPersonality::Player => ShipController::new_null(),
|
ShipPersonality::Dummy | ShipPersonality::Player => ShipController::new_null(),
|
||||||
ShipPersonality::Point => ShipController::new_point(),
|
ShipPersonality::Point => ShipController::new_point(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
self.ships.insert(
|
self.ships.insert(
|
||||||
ship.get_handle(),
|
collider,
|
||||||
SySimShip::new(ct, ship.get_handle(), ship.get_faction(), r, c),
|
SySimShip::new(ct, handle, personality, faction, ridid_body, collider),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return SySimShipHandle(collider);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update a player ship's controls
|
/// Update a player ship's controls
|
||||||
|
@ -254,69 +235,59 @@ impl SystemSim {
|
||||||
// TODO: don't allocate!
|
// TODO: don't allocate!
|
||||||
let mut projectiles = Vec::new();
|
let mut projectiles = Vec::new();
|
||||||
let mut to_remove = 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...
|
// Borrow immutably for now...
|
||||||
// (required to compute controls)
|
// (required to compute controls)
|
||||||
let ship_object = self.ships.get(handle).unwrap();
|
let ship = self.ships.get(&collider).unwrap();
|
||||||
if ship_object.should_be_removed() {
|
if ship.data.get_state().should_be_removed() {
|
||||||
to_remove.push(ship_object.collider);
|
to_remove.push(collider);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Short-circuit continue if this ship isn't in game data
|
// This ship is playing a collapse sequence
|
||||||
// (which means it's playing a collapse sequence)
|
// and needs no additional logic
|
||||||
if res.gx.get_ship(*handle).is_none() {
|
if ship.data.get_state().is_collapsing() {
|
||||||
let ship_object = self.ships.get_mut(handle).unwrap();
|
let ship = self.ships.get_mut(&collider).unwrap();
|
||||||
ship_object.step(
|
ship.step(
|
||||||
res,
|
res,
|
||||||
&mut self.rigid_body_set[ship_object.rigid_body],
|
&mut self.rigid_body_set[ship.rigid_body],
|
||||||
&mut self.collider_set[ship_object.collider],
|
&mut self.collider_set[ship.collider],
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute new controls
|
// Compute new controls
|
||||||
let controls;
|
let controls;
|
||||||
let b = self.ship_behaviors.get_mut(handle).unwrap();
|
let b = self.ship_behaviors.get_mut(&collider).unwrap();
|
||||||
controls = b.update_controls(
|
controls = b.update_controls(&res, &self.rigid_body_set, &self.ships, ship.collider);
|
||||||
&res,
|
|
||||||
&self.rigid_body_set,
|
let ship = self.ships.get_mut(&collider).unwrap();
|
||||||
&self.ships,
|
|
||||||
ship_object.rigid_body,
|
|
||||||
*handle,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Now re-borrow mutably to apply changes
|
|
||||||
let ship_object = self.ships.get_mut(handle).unwrap();
|
|
||||||
if let Some(controls) = controls {
|
if let Some(controls) = controls {
|
||||||
ship_object.controls = controls;
|
ship.controls = controls;
|
||||||
}
|
}
|
||||||
ship_object.step(
|
ship.step(
|
||||||
res,
|
res,
|
||||||
&mut self.rigid_body_set[ship_object.rigid_body],
|
&mut self.rigid_body_set[ship.rigid_body],
|
||||||
&mut self.collider_set[ship_object.collider],
|
&mut self.collider_set[ship.collider],
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we're firing, try to fire each gun
|
// If we're firing, try to fire each gun
|
||||||
if ship_object.controls.guns {
|
if ship.controls.guns {
|
||||||
let ship_data = res.gx.get_ship_mut(ship_object.data_handle).unwrap();
|
|
||||||
|
|
||||||
// TODO: don't allocate here. This is a hack to satisfy the borrow checker,
|
// TODO: don't allocate here. This is a hack to satisfy the borrow checker,
|
||||||
// convert this to a refcell or do the replace dance.
|
// convert this to a refcell or do the replace dance.
|
||||||
let pairs: Vec<(GunPoint, Option<OutfitHandle>)> = ship_data
|
let pairs: Vec<(GunPoint, Option<OutfitHandle>)> = ship
|
||||||
|
.data
|
||||||
.get_outfits()
|
.get_outfits()
|
||||||
.iter_gun_points()
|
.iter_gun_points()
|
||||||
.map(|(p, o)| (p.clone(), o.clone()))
|
.map(|(p, o)| (p.clone(), o.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for (gun, outfit) in pairs {
|
for (gun, outfit) in pairs {
|
||||||
if ship_data.fire_gun(res.ct, &gun) {
|
if ship.data.fire_gun(res.ct, &gun) {
|
||||||
projectiles.push((
|
projectiles.push((ship.collider, gun.clone(), outfit.unwrap()));
|
||||||
ship_object.data_handle,
|
|
||||||
ship_object.rigid_body,
|
|
||||||
gun.clone(),
|
|
||||||
outfit.unwrap(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,11 +299,11 @@ impl SystemSim {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create projectiles
|
// 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 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_pos = util::rigidbody_position(rigid_body);
|
||||||
let ship_rot = util::rigidbody_rotation(rigid_body);
|
let ship_rot = util::rigidbody_rotation(rigid_body);
|
||||||
let ship_vel = util::rigidbody_velocity(rigid_body);
|
let ship_vel = util::rigidbody_velocity(rigid_body);
|
||||||
|
@ -382,7 +353,7 @@ impl SystemSim {
|
||||||
SySimProjectile::new(
|
SySimProjectile::new(
|
||||||
outfit.projectile.clone(),
|
outfit.projectile.clone(),
|
||||||
rigid_body,
|
rigid_body,
|
||||||
ship_dat.get_faction(),
|
ship.data.get_faction(),
|
||||||
collider,
|
collider,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -416,11 +387,7 @@ impl SystemSim {
|
||||||
};
|
};
|
||||||
|
|
||||||
let p = self.projectiles.get(&a);
|
let p = self.projectiles.get(&a);
|
||||||
let s = self.ships.get_mut(match self.collider_ship_table.get(&b) {
|
let s = self.ships.get_mut(&b);
|
||||||
// If none, this ship is dead.
|
|
||||||
Some(x) => x,
|
|
||||||
None => continue,
|
|
||||||
});
|
|
||||||
if p.is_none() || s.is_none() {
|
if p.is_none() || s.is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -476,8 +443,13 @@ impl SystemSim {
|
||||||
// Public getters
|
// Public getters
|
||||||
impl SystemSim {
|
impl SystemSim {
|
||||||
/// Get a ship physics object
|
/// Get a ship physics object
|
||||||
pub fn get_ship(&self, ship: GxShipHandle) -> Option<&SySimShip> {
|
pub fn get_ship(&self, ship: &SySimShipHandle) -> Option<&SySimShip> {
|
||||||
self.ships.get(&ship)
|
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
|
/// Get a rigid body from a handle
|
||||||
|
|
|
@ -9,10 +9,6 @@ pub struct Timing {
|
||||||
pub frame: Duration,
|
pub frame: Duration,
|
||||||
frame_timer: Instant,
|
frame_timer: Instant,
|
||||||
|
|
||||||
/// The time we spent simulating game state
|
|
||||||
pub galaxy: Duration,
|
|
||||||
galaxy_timer: Instant,
|
|
||||||
|
|
||||||
/// The time we spent simulating physics
|
/// The time we spent simulating physics
|
||||||
pub physics_sim: Duration,
|
pub physics_sim: Duration,
|
||||||
physics_sim_timer: Instant,
|
physics_sim_timer: Instant,
|
||||||
|
@ -29,10 +25,8 @@ impl Timing {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
frame: Duration::ZERO,
|
frame: Duration::ZERO,
|
||||||
galaxy: Duration::ZERO,
|
|
||||||
physics_sim: Duration::ZERO,
|
physics_sim: Duration::ZERO,
|
||||||
physics_ship: Duration::ZERO,
|
physics_ship: Duration::ZERO,
|
||||||
galaxy_timer: Instant::now(),
|
|
||||||
physics_sim_timer: Instant::now(),
|
physics_sim_timer: Instant::now(),
|
||||||
physics_ship_timer: Instant::now(),
|
physics_ship_timer: Instant::now(),
|
||||||
frame_timer: Instant::now(),
|
frame_timer: Instant::now(),
|
||||||
|
@ -44,11 +38,6 @@ impl Timing {
|
||||||
self.frame_timer = Instant::now();
|
self.frame_timer = Instant::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start galaxy timer
|
|
||||||
pub fn start_galaxy(&mut self) {
|
|
||||||
self.galaxy_timer = Instant::now();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start physics sim timer
|
/// Start physics sim timer
|
||||||
pub fn start_physics_sim(&mut self) {
|
pub fn start_physics_sim(&mut self) {
|
||||||
self.physics_sim_timer = Instant::now();
|
self.physics_sim_timer = Instant::now();
|
||||||
|
@ -64,11 +53,6 @@ impl Timing {
|
||||||
self.frame = self.frame_timer.elapsed();
|
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
|
/// Record physics simulation time
|
||||||
pub fn mark_physics_sim(&mut self) {
|
pub fn mark_physics_sim(&mut self) {
|
||||||
self.physics_sim = self.physics_sim_timer.elapsed();
|
self.physics_sim = self.physics_sim_timer.elapsed();
|
||||||
|
|
Loading…
Reference in New Issue