Galactica/crates/gameobject/src/ship/outfitset.rs

233 lines
6.1 KiB
Rust
Raw Normal View History

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),
2024-01-09 17:23:54 -08:00
/// An outfit couldn't be added because there weren't enough points for it
/// (e.g, gun points, turret points, etc)
NotEnoughPoints(String),
2024-01-08 22:38:36 -08:00
}
/// 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 17:23:54 -08:00
/// This is a fairly static data structure: it does not keep track of cooldowns,
/// shield damage, etc. It only provides an interface for static stats which are
/// then used elsewhere.
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 {
2024-01-09 17:23:54 -08:00
pub(super) fn new(available_space: OutfitSpace, gun_points: &[GunPoint]) -> Self {
2024-01-08 22:38:36 -08:00
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(),
}
}
2024-01-09 17:23:54 -08:00
pub(super) fn add(&mut self, o: &content::Outfit) -> OutfitAddResult {
2024-01-08 22:38:36 -08:00
if !(self.total_space - self.used_space).can_contain(&o.space) {
return OutfitAddResult::NotEnoughSpace("TODO".to_string());
}
2024-01-09 17:23:54 -08:00
// Check and return as fast as possible,
// BEFORE we make any changes.
if o.gun.is_some() {
let mut added = false;
for (_, outfit) in &mut self.gun_points {
if outfit.is_none() {
*outfit = Some(o.handle);
added = true;
}
}
if !added {
return OutfitAddResult::NotEnoughPoints("gun".to_string());
}
}
2024-01-08 22:38:36 -08:00
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;
}
2024-01-09 17:23:54 -08:00
pub(super) fn remove(&mut self, o: &content::Outfit) -> OutfitRemoveResult {
2024-01-08 22:38:36 -08:00
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()
}
2024-01-09 17:23:54 -08:00
/// Get the outfit attached to the given gun point
/// Will panic if this gunpoint is not in this outfit set.
pub fn get_gun(&self, point: &GunPoint) -> Option<OutfitHandle> {
self.gun_points.get(point).unwrap().clone()
}
2024-01-08 22:38:36 -08:00
/// 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
}
}