197 lines
4.6 KiB
Rust
197 lines
4.6 KiB
Rust
use cgmath::{Deg, Point3};
|
|
use galactica_render::ObjectSubSprite;
|
|
|
|
use crate::content;
|
|
|
|
/// Represents a gun attached to a specific ship at a certain gunpoint.
|
|
#[derive(Debug)]
|
|
pub struct ShipGun {
|
|
pub kind: content::Gun,
|
|
pub cooldown: f32,
|
|
pub point: usize,
|
|
}
|
|
|
|
impl ShipGun {
|
|
pub fn new(kind: content::Gun, point: usize) -> Self {
|
|
Self {
|
|
kind: kind,
|
|
point,
|
|
cooldown: 0.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// This struct keeps track of the combined stats of a set of outfits.
|
|
/// It does NOT check for sanity (e.g, removing an outfit that was never added)
|
|
/// That is handled by ShipOutfits.
|
|
#[derive(Debug)]
|
|
pub struct OutfitStatSum {
|
|
pub engine_thrust: f32,
|
|
pub steer_power: f32,
|
|
pub engine_flare_textures: Vec<content::TextureHandle>,
|
|
}
|
|
|
|
impl OutfitStatSum {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
engine_thrust: 0.0,
|
|
steer_power: 0.0,
|
|
engine_flare_textures: Vec::new(),
|
|
}
|
|
}
|
|
|
|
pub fn add(&mut self, o: &content::Outfit) {
|
|
self.engine_thrust += o.engine_thrust;
|
|
if let Some(t) = o.engine_flare_texture {
|
|
self.engine_flare_textures.push(t);
|
|
};
|
|
self.steer_power += o.steer_power;
|
|
}
|
|
|
|
pub fn remove(&mut self, o: &content::Outfit) {
|
|
self.engine_thrust -= o.engine_thrust;
|
|
if let Some(t) = o.engine_flare_texture {
|
|
self.engine_flare_textures.remove(
|
|
self.engine_flare_textures
|
|
.iter()
|
|
.position(|x| *x == t)
|
|
.unwrap(),
|
|
);
|
|
}
|
|
self.steer_power -= o.steer_power;
|
|
}
|
|
}
|
|
|
|
/// Represents all the outfits attached to a ship.
|
|
/// Keeps track of the sum of their stats, so it mustn't be re-computed every frame.
|
|
#[derive(Debug)]
|
|
pub struct ShipOutfits {
|
|
pub stats: OutfitStatSum,
|
|
pub total_space: content::OutfitSpace,
|
|
|
|
available_space: content::OutfitSpace,
|
|
outfits: Vec<content::Outfit>,
|
|
guns: Vec<ShipGun>,
|
|
enginepoints: Vec<content::EnginePoint>,
|
|
gunpoints: Vec<content::GunPoint>,
|
|
|
|
// Minor performance optimization, since we
|
|
// rarely need to re-compute these.
|
|
engine_flare_sprites: Vec<ObjectSubSprite>,
|
|
}
|
|
|
|
impl<'a> ShipOutfits {
|
|
pub fn new(content: &content::Ship) -> Self {
|
|
Self {
|
|
stats: OutfitStatSum::new(),
|
|
outfits: Vec::new(),
|
|
guns: Vec::new(),
|
|
available_space: content.space.clone(),
|
|
total_space: content.space.clone(),
|
|
enginepoints: content.engines.clone(),
|
|
gunpoints: content.guns.clone(),
|
|
engine_flare_sprites: vec![],
|
|
}
|
|
}
|
|
|
|
/// Does this outfit set contain the specified outfit?
|
|
pub fn contains_outfit(&self, o: &content::Outfit) -> bool {
|
|
match self.outfits.iter().position(|x| x.name == o.name) {
|
|
Some(_) => true,
|
|
None => false,
|
|
}
|
|
}
|
|
|
|
/// Add an outfit to this ship.
|
|
/// Returns true on success, and false on failure
|
|
/// TODO: failure reason enum
|
|
pub fn add(&mut self, o: content::Outfit) -> bool {
|
|
if !self.available_space.can_contain(o.space) {
|
|
return false;
|
|
}
|
|
self.available_space.occupy(&o.space);
|
|
self.stats.add(&o);
|
|
self.outfits.push(o);
|
|
self.update_engine_flares();
|
|
return true;
|
|
}
|
|
|
|
/// TODO: is outfit in set?
|
|
pub fn remove(&mut self, o: content::Outfit) {
|
|
let i = match self.outfits.iter().position(|x| x.name == o.name) {
|
|
Some(i) => i,
|
|
None => panic!("Removed non-existing outfit"),
|
|
};
|
|
self.available_space.free(&o.space);
|
|
self.outfits.remove(i);
|
|
self.stats.remove(&o);
|
|
self.update_engine_flares();
|
|
}
|
|
|
|
/// Add a gun to this outfit set.
|
|
/// This automatically attaches the gun to the first available gunpoint,
|
|
/// and returns false (applying no changes) if no points are available.
|
|
pub fn add_gun(&mut self, gun: content::Gun) -> bool {
|
|
// Find first unused point
|
|
let mut p = None;
|
|
'outer: for i in 0..self.gunpoints.len() {
|
|
for g in &self.guns {
|
|
if g.point == i {
|
|
continue 'outer;
|
|
}
|
|
}
|
|
p = Some(i);
|
|
break;
|
|
}
|
|
|
|
// All points are taken
|
|
if p.is_none() {
|
|
return false;
|
|
}
|
|
|
|
let sg = ShipGun::new(gun, p.unwrap());
|
|
self.guns.push(sg);
|
|
return true;
|
|
}
|
|
|
|
pub fn iter_guns(&mut self) -> impl Iterator<Item = &mut ShipGun> {
|
|
self.guns.iter_mut()
|
|
}
|
|
|
|
pub fn iter_guns_points(&mut self) -> impl Iterator<Item = (&mut ShipGun, &content::GunPoint)> {
|
|
self.guns
|
|
.iter_mut()
|
|
.map(|x| (&self.gunpoints[x.point], x))
|
|
.map(|(a, b)| (b, a))
|
|
}
|
|
|
|
pub fn update_engine_flares(&mut self) {
|
|
// TODO: better way to pick flare texture
|
|
self.engine_flare_sprites.clear();
|
|
let t = if let Some(e) = self.stats.engine_flare_textures.iter().next() {
|
|
e
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
self.engine_flare_sprites = self
|
|
.enginepoints
|
|
.iter()
|
|
.map(|p| ObjectSubSprite {
|
|
pos: Point3 {
|
|
x: p.pos.x,
|
|
y: p.pos.y,
|
|
z: 1.0,
|
|
},
|
|
texture: *t,
|
|
angle: Deg(0.0),
|
|
size: p.size,
|
|
})
|
|
.collect();
|
|
}
|
|
|
|
pub fn get_engine_flares(&self) -> Vec<ObjectSubSprite> {
|
|
return self.engine_flare_sprites.clone();
|
|
}
|
|
}
|