Galactica/crates/gameobject/src/outfits.rs

215 lines
5.3 KiB
Rust
Raw Normal View History

2023-12-28 17:04:41 -08:00
use cgmath::{Deg, Point3};
2024-01-01 11:29:48 -08:00
use galactica_content as content;
2023-12-31 18:39:37 -08:00
use galactica_render::ObjectSubSprite;
2023-12-28 17:04:41 -08:00
2023-12-28 17:06:33 -08:00
/// Represents a gun attached to a specific ship at a certain gunpoint.
2023-12-30 20:27:53 -08:00
#[derive(Debug)]
2023-12-28 17:04:41 -08:00
pub struct ShipGun {
2024-01-01 12:01:29 -08:00
/// The kind of gun this is
2023-12-28 17:04:41 -08:00
pub kind: content::Gun,
2024-01-01 12:01:29 -08:00
/// How many seconds we must wait before this gun can fire
2023-12-28 17:04:41 -08:00
pub cooldown: f32,
2024-01-01 12:01:29 -08:00
/// Index of the gunpoint this gun is attached to
2023-12-28 17:04:41 -08:00
pub point: usize,
}
impl ShipGun {
2024-01-01 12:01:29 -08:00
/// Make a new shipgun
2024-01-01 15:41:47 -08:00
pub fn new(kind: &content::Gun, point: usize) -> Self {
2023-12-28 17:04:41 -08:00
Self {
2024-01-01 15:41:47 -08:00
kind: kind.clone(),
2023-12-28 17:04:41 -08:00
point,
cooldown: 0.0,
}
}
}
2023-12-30 20:27:53 -08:00
/// 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_sprites: Vec<content::SpriteHandle>,
2023-12-28 17:04:41 -08:00
}
2023-12-30 20:27:53 -08:00
impl OutfitStatSum {
pub fn new() -> Self {
Self {
engine_thrust: 0.0,
steer_power: 0.0,
engine_flare_sprites: Vec::new(),
2023-12-28 17:04:41 -08:00
}
}
2023-12-30 20:27:53 -08:00
pub fn add(&mut self, o: &content::Outfit) {
self.engine_thrust += o.engine_thrust;
if let Some(t) = o.engine_flare_sprite {
self.engine_flare_sprites.push(t);
2023-12-30 20:27:53 -08:00
};
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_sprite {
self.engine_flare_sprites.remove(
self.engine_flare_sprites
2023-12-30 20:27:53 -08:00
.iter()
.position(|x| *x == t)
.unwrap(),
);
2023-12-28 17:04:41 -08:00
}
2023-12-30 20:27:53 -08:00
self.steer_power -= o.steer_power;
2023-12-28 17:04:41 -08:00
}
}
2024-01-01 15:41:47 -08:00
/// Represents a set of outfits attached to a ship.
2023-12-30 20:27:53 -08:00
/// Keeps track of the sum of their stats, so it mustn't be re-computed every frame.
#[derive(Debug)]
2024-01-01 15:41:47 -08:00
pub struct OutfitSet {
2024-01-01 12:01:29 -08:00
/// The total sum of the stats this set of outfits provides
2024-01-03 06:37:02 -08:00
stats: OutfitStatSum,
2023-12-30 21:05:06 -08:00
2024-01-01 12:01:29 -08:00
//pub total_space: content::OutfitSpace,
2023-12-31 18:39:37 -08:00
available_space: content::OutfitSpace,
2024-01-01 15:41:47 -08:00
outfits: Vec<content::OutfitHandle>,
2023-12-30 20:27:53 -08:00
guns: Vec<ShipGun>,
2023-12-28 17:04:41 -08:00
enginepoints: Vec<content::EnginePoint>,
gunpoints: Vec<content::GunPoint>,
// Minor performance optimization, since we
// rarely need to re-compute these.
2023-12-31 17:58:17 -08:00
engine_flare_sprites: Vec<ObjectSubSprite>,
2023-12-28 17:04:41 -08:00
}
2024-01-01 15:41:47 -08:00
impl<'a> OutfitSet {
2024-01-01 12:01:29 -08:00
/// Make a new outfit array
pub fn new(content: &content::Ship) -> Self {
2023-12-28 17:04:41 -08:00
Self {
2023-12-30 20:27:53 -08:00
stats: OutfitStatSum::new(),
2023-12-28 17:04:41 -08:00
outfits: Vec::new(),
2023-12-30 20:27:53 -08:00
guns: Vec::new(),
2023-12-30 21:05:06 -08:00
available_space: content.space.clone(),
2024-01-01 12:01:29 -08:00
//total_space: content.space.clone(),
enginepoints: content.engines.clone(),
gunpoints: content.guns.clone(),
2023-12-28 17:04:41 -08:00
engine_flare_sprites: vec![],
}
}
2023-12-30 21:05:06 -08:00
/// Does this outfit set contain the specified outfit?
2024-01-01 15:41:47 -08:00
pub fn contains_outfit(&self, o: content::OutfitHandle) -> bool {
match self.outfits.iter().position(|x| *x == o) {
2023-12-30 21:05:06 -08:00
Some(_) => true,
None => false,
}
}
2024-01-03 06:37:02 -08:00
pub fn stat_sum(&self) -> &OutfitStatSum {
&self.stats
}
2023-12-30 20:27:53 -08:00
/// Add an outfit to this ship.
/// Returns true on success, and false on failure
/// TODO: failure reason enum
2024-01-01 15:41:47 -08:00
pub fn add(&mut self, ct: &content::Content, o: content::OutfitHandle) -> bool {
let outfit = ct.get_outfit(o);
if !self.available_space.can_contain(&outfit.space) {
2023-12-30 21:05:06 -08:00
return false;
}
2024-01-01 15:41:47 -08:00
self.available_space.occupy(&outfit.space);
self.stats.add(&outfit);
2023-12-28 17:04:41 -08:00
self.outfits.push(o);
self.update_engine_flares();
2023-12-30 20:27:53 -08:00
return true;
2023-12-28 17:04:41 -08:00
}
2024-01-01 15:41:47 -08:00
/// Remove an outfit from this set
pub fn remove(&mut self, ct: &content::Content, o: content::OutfitHandle) {
let outfit = ct.get_outfit(o);
let i = match self.outfits.iter().position(|x| *x == o) {
2023-12-30 21:05:06 -08:00
Some(i) => i,
2024-01-01 15:41:47 -08:00
None => panic!("removed non-existing outfit"),
2023-12-30 21:05:06 -08:00
};
2024-01-01 15:41:47 -08:00
self.available_space.free(&outfit.space);
2023-12-30 21:05:06 -08:00
self.outfits.remove(i);
2024-01-01 15:41:47 -08:00
self.stats.remove(&outfit);
2023-12-30 20:27:53 -08:00
self.update_engine_flares();
2023-12-28 17:04:41 -08:00
}
2023-12-30 20:27:53 -08:00
/// 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.
2024-01-01 15:41:47 -08:00
pub fn add_gun(&mut self, ct: &content::Content, g: content::GunHandle) -> bool {
2023-12-30 20:27:53 -08:00
// 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;
}
2024-01-01 15:41:47 -08:00
let gun = ct.get_gun(g);
2023-12-30 20:27:53 -08:00
// All points are taken
if p.is_none() {
return false;
}
let sg = ShipGun::new(gun, p.unwrap());
self.guns.push(sg);
return true;
2023-12-28 17:04:41 -08:00
}
2024-01-01 12:01:29 -08:00
/// Iterate over all guns in this outfitset
2023-12-30 20:27:53 -08:00
pub fn iter_guns(&mut self) -> impl Iterator<Item = &mut ShipGun> {
self.guns.iter_mut()
2023-12-28 17:04:41 -08:00
}
2024-01-01 12:01:29 -08:00
/// Iterate over all guns and the gunpoints they're attached to
2023-12-30 20:27:53 -08:00
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))
2023-12-28 17:04:41 -08:00
}
2024-01-01 12:01:29 -08:00
/// Update engine flare sprites
2023-12-28 17:04:41 -08:00
pub fn update_engine_flares(&mut self) {
// TODO: better way to pick flare texture
self.engine_flare_sprites.clear();
let s = if let Some(e) = self.stats.engine_flare_sprites.iter().next() {
2023-12-30 20:27:53 -08:00
e
2023-12-28 17:04:41 -08:00
} else {
return;
};
self.engine_flare_sprites = self
2023-12-30 20:27:53 -08:00
.enginepoints
.iter()
2023-12-31 17:58:17 -08:00
.map(|p| ObjectSubSprite {
2023-12-28 17:04:41 -08:00
pos: Point3 {
x: p.pos.x,
y: p.pos.y,
z: 1.0,
},
sprite: *s,
2023-12-28 17:04:41 -08:00
angle: Deg(0.0),
size: p.size,
})
.collect();
}
2024-01-01 12:01:29 -08:00
/// Get the sprites we should show if this ship is firing its engines
2023-12-31 17:58:17 -08:00
pub fn get_engine_flares(&self) -> Vec<ObjectSubSprite> {
2023-12-28 17:04:41 -08:00
return self.engine_flare_sprites.clone();
}
}