2024-01-08 22:38:36 -08:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2024-01-09 14:13:16 -08:00
|
|
|
use content::{GunPoint, OutfitHandle, OutfitSpace, SpriteHandle};
|
2024-01-08 22:38:36 -08:00
|
|
|
use galactica_content as content;
|
|
|
|
|
|
|
|
/// Possible outcomes when adding an outfit
|
|
|
|
pub enum OutfitAddResult {
|
|
|
|
/// An outfit was successfully added
|
|
|
|
Ok,
|
|
|
|
|
|
|
|
/// An outfit could not be added because we don't have enough free space.
|
|
|
|
/// The string tells us what kind of space we need:
|
|
|
|
/// `outfit,` `weapon,` `engine,` etc. Note that these sometimes overlap:
|
|
|
|
/// outfits may need outfit AND weapon space. In these cases, this result
|
|
|
|
/// should name the "most specific" kind of space we lack.
|
|
|
|
NotEnoughSpace(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Possible outcomes when removing an outfit
|
|
|
|
pub enum OutfitRemoveResult {
|
|
|
|
/// This outfit was successfully removed
|
|
|
|
Ok,
|
|
|
|
|
|
|
|
/// This outfit isn't in this set
|
|
|
|
NotExist,
|
|
|
|
// TODO:
|
|
|
|
// This is where we'll add non-removable outfits,
|
|
|
|
// outfits that provide space, etc
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A simple data class, used to keep track of delayed shield generators
|
2024-01-09 11:34:54 -08:00
|
|
|
#[derive(Debug, Clone)]
|
2024-01-08 22:38:36 -08:00
|
|
|
pub(crate) struct ShieldGenerator {
|
|
|
|
pub outfit: OutfitHandle,
|
|
|
|
pub delay: f32,
|
|
|
|
pub generation: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This struct keeps track of a ship's outfit loadout.
|
2024-01-09 11:34:54 -08:00
|
|
|
#[derive(Debug, Clone)]
|
2024-01-08 22:38:36 -08:00
|
|
|
pub struct OutfitSet {
|
|
|
|
/// What outfits does this statsum contain?
|
|
|
|
outfits: HashMap<OutfitHandle, u32>,
|
|
|
|
|
|
|
|
/// Space available in this outfitset.
|
|
|
|
/// set at creation and never changes.
|
|
|
|
total_space: OutfitSpace,
|
|
|
|
|
|
|
|
/// Space used by the outfits in this set.
|
|
|
|
/// This may be negative if certain outfits provide space!
|
|
|
|
used_space: OutfitSpace,
|
|
|
|
|
|
|
|
/// The gun points available in this ship.
|
|
|
|
/// If value is None, this point is free.
|
|
|
|
/// if value is Some, this point is taken.
|
|
|
|
gun_points: HashMap<GunPoint, Option<OutfitHandle>>,
|
|
|
|
|
|
|
|
// Outfit values
|
|
|
|
// This isn't strictly necessary, but we don't want to
|
|
|
|
// re-compute this on each frame.
|
|
|
|
engine_thrust: f32,
|
|
|
|
steer_power: f32,
|
|
|
|
shield_strength: f32,
|
|
|
|
|
|
|
|
// Delay, generation
|
|
|
|
// TODO: struct
|
|
|
|
shield_generators: Vec<ShieldGenerator>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl OutfitSet {
|
|
|
|
pub fn new(available_space: OutfitSpace, gun_points: &[GunPoint]) -> Self {
|
|
|
|
Self {
|
|
|
|
outfits: HashMap::new(),
|
|
|
|
total_space: available_space,
|
|
|
|
used_space: OutfitSpace::new(),
|
|
|
|
gun_points: gun_points.iter().map(|x| (x.clone(), None)).collect(),
|
|
|
|
|
|
|
|
engine_thrust: 0.0,
|
|
|
|
steer_power: 0.0,
|
|
|
|
shield_strength: 0.0,
|
|
|
|
shield_generators: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add(&mut self, o: &content::Outfit) -> OutfitAddResult {
|
|
|
|
if !(self.total_space - self.used_space).can_contain(&o.space) {
|
|
|
|
return OutfitAddResult::NotEnoughSpace("TODO".to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
self.used_space += o.space;
|
|
|
|
|
|
|
|
self.engine_thrust += o.engine_thrust;
|
|
|
|
self.steer_power += o.steer_power;
|
|
|
|
self.shield_strength += o.shield_strength;
|
|
|
|
self.shield_generators.push(ShieldGenerator {
|
|
|
|
outfit: o.handle,
|
|
|
|
delay: o.shield_delay,
|
|
|
|
generation: o.shield_generation,
|
|
|
|
});
|
|
|
|
|
2024-01-09 14:13:16 -08:00
|
|
|
if self.outfits.contains_key(&o.handle) {
|
|
|
|
*self.outfits.get_mut(&o.handle).unwrap() += 1;
|
|
|
|
} else {
|
|
|
|
self.outfits.insert(o.handle, 1);
|
|
|
|
}
|
|
|
|
|
2024-01-08 22:38:36 -08:00
|
|
|
return OutfitAddResult::Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove(&mut self, o: &content::Outfit) -> OutfitRemoveResult {
|
|
|
|
if !self.outfits.contains_key(&o.handle) {
|
|
|
|
return OutfitRemoveResult::NotExist;
|
|
|
|
} else {
|
|
|
|
let n = *self.outfits.get(&o.handle).unwrap();
|
|
|
|
if n == 1u32 {
|
|
|
|
self.outfits.remove(&o.handle);
|
|
|
|
} else {
|
|
|
|
*self.outfits.get_mut(&o.handle).unwrap() -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.used_space -= o.space;
|
|
|
|
|
|
|
|
self.engine_thrust -= o.engine_thrust;
|
|
|
|
self.steer_power -= o.steer_power;
|
|
|
|
self.shield_strength -= o.shield_strength;
|
|
|
|
|
|
|
|
{
|
|
|
|
// This index will exist, since we checked the hashmap
|
|
|
|
let index = self
|
|
|
|
.shield_generators
|
|
|
|
.iter()
|
|
|
|
.position(|g| g.outfit == o.handle)
|
|
|
|
.unwrap();
|
|
|
|
self.shield_generators.remove(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
return OutfitRemoveResult::Ok;
|
|
|
|
}
|
2024-01-09 14:13:16 -08:00
|
|
|
|
|
|
|
// TODO: pick these better
|
|
|
|
pub fn get_flare_sprite(&self, ct: &content::Content) -> Option<SpriteHandle> {
|
|
|
|
for i in self.outfits.keys() {
|
|
|
|
let c = ct.get_outfit(*i);
|
|
|
|
if c.engine_flare_sprite.is_some() {
|
|
|
|
return c.engine_flare_sprite;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return None;
|
|
|
|
}
|
2024-01-08 22:38:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Simple getters to make sure nobody meddles with our internal state
|
|
|
|
impl OutfitSet {
|
|
|
|
/// The number of outfits in this set
|
|
|
|
pub fn len(&self) -> u32 {
|
|
|
|
self.outfits.iter().map(|(_, x)| x).sum()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Iterate over all outfits
|
|
|
|
pub fn iter_outfits(&self) -> impl Iterator<Item = (&OutfitHandle, &u32)> {
|
|
|
|
self.outfits.iter()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Iterate over all gun points
|
|
|
|
pub fn iter_gun_points(&self) -> impl Iterator<Item = (&GunPoint, &Option<OutfitHandle>)> {
|
|
|
|
self.gun_points.iter()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Iterate over all shield generators
|
|
|
|
pub(crate) fn iter_shield_generators(&self) -> impl Iterator<Item = &ShieldGenerator> {
|
|
|
|
self.shield_generators.iter()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get maximum possible shield regen
|
|
|
|
pub fn get_max_shield_regen(&self) -> f32 {
|
|
|
|
self.shield_generators.iter().map(|x| x.generation).sum()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Total available outfit space
|
|
|
|
pub fn get_total_space(&self) -> &OutfitSpace {
|
|
|
|
&self.total_space
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Used outfit space
|
|
|
|
pub fn get_used_space(&self) -> &OutfitSpace {
|
|
|
|
&self.used_space
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Total foward thrust
|
|
|
|
pub fn get_engine_thrust(&self) -> f32 {
|
|
|
|
self.engine_thrust
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Total steer power
|
|
|
|
pub fn get_steer_power(&self) -> f32 {
|
|
|
|
self.steer_power
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Total shield strength
|
|
|
|
pub fn get_shield_strength(&self) -> f32 {
|
|
|
|
self.shield_strength
|
|
|
|
}
|
|
|
|
}
|