use std::collections::HashMap; use content::{GunPoint, OutfitHandle, OutfitSpace, SpriteHandle}; 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 #[derive(Debug, Clone)] pub(crate) struct ShieldGenerator { pub outfit: OutfitHandle, pub delay: f32, pub generation: f32, } /// This struct keeps track of a ship's outfit loadout. #[derive(Debug, Clone)] pub struct OutfitSet { /// What outfits does this statsum contain? outfits: HashMap, /// 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>, // 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, } 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, }); if self.outfits.contains_key(&o.handle) { *self.outfits.get_mut(&o.handle).unwrap() += 1; } else { self.outfits.insert(o.handle, 1); } 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; } // TODO: pick these better pub fn get_flare_sprite(&self, ct: &content::Content) -> Option { 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; } } // 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 { self.outfits.iter() } /// Iterate over all gun points pub fn iter_gun_points(&self) -> impl Iterator)> { self.gun_points.iter() } /// Iterate over all shield generators pub(crate) fn iter_shield_generators(&self) -> impl Iterator { 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 } }