use std::collections::HashMap;
use crate::game_params::ttx::constants::DispersionCurve;
use crate::game_params::ttx::model::DegreesPerSecond;
use crate::game_params::ttx::model::Hp;
use crate::game_params::ttx::model::Knots;
use crate::game_params::ttx::model::Seconds;
use crate::game_params::types::Km;
use crate::game_params::types::Meters;
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
pub struct HullComponentStats {
pub health: Option<Hp>,
pub max_speed: Option<Knots>,
pub speed_coef: Option<f32>,
pub turning_radius: Option<Meters>,
pub rudder_time: Option<Seconds>,
pub visibility_factor: Option<Km>,
pub visibility_factor_by_plane: Option<Km>,
pub visibility_coef_fire: Option<Km>,
pub visibility_coef_fire_by_plane: Option<Km>,
pub visibility_coef_gk: Option<Km>,
pub visibility_coef_gk_in_smoke: Option<Km>,
pub visibility_factor_by_periscope: Option<Km>,
pub flood_prob: Option<f32>,
pub battery_capacity: Option<f32>,
pub battery_regen_rate: Option<f32>,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
pub struct EngineComponentStats {
pub speed_coef: Option<f32>,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
pub struct TorpedoLauncherStats {
pub shot_delay: Option<Seconds>,
pub rotation_speed: Option<DegreesPerSecond>,
pub num_barrels: Option<f32>,
pub ammo_switch_coeff: Option<f32>,
pub ammo: Vec<String>,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
pub struct ArtilleryGunStats {
pub shot_delay: Option<Seconds>,
pub rotation_speed: Option<DegreesPerSecond>,
pub num_barrels: Option<f32>,
pub barrel_diameter: Option<Meters>,
pub ammo_switch_coeff: Option<f32>,
pub min_radius: Option<f32>,
pub ideal_radius: Option<f32>,
pub ideal_distance: Option<f32>,
pub radius_on_zero: Option<f32>,
pub radius_on_delim: Option<f32>,
pub radius_on_max: Option<f32>,
pub delim: Option<f32>,
pub ammo: Vec<String>,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
pub struct ArtilleryComponentStats {
pub max_dist: Option<Meters>,
pub guns: Vec<ArtilleryGunStats>,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
pub struct SecondaryComponentStats {
pub max_dist: Option<Meters>,
pub guns: Vec<ArtilleryGunStats>,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
pub struct ShipTtxComponents {
#[cfg_attr(feature = "serde", serde(default))]
pub hulls: HashMap<String, HullComponentStats>,
#[cfg_attr(feature = "serde", serde(default))]
pub engines: HashMap<String, EngineComponentStats>,
#[cfg_attr(feature = "serde", serde(default))]
pub torpedoes: HashMap<String, Vec<TorpedoLauncherStats>>,
#[cfg_attr(feature = "serde", serde(default))]
pub artillery: HashMap<String, ArtilleryComponentStats>,
#[cfg_attr(feature = "serde", serde(default))]
pub secondaries: HashMap<String, SecondaryComponentStats>,
#[cfg_attr(feature = "serde", serde(default))]
pub fire_controls: HashMap<String, f32>,
#[cfg_attr(feature = "serde", serde(default))]
pub stock_selection: super::selection::ShipUpgradeSelection,
}
impl ShipTtxComponents {
pub fn is_empty(&self) -> bool {
self.hulls.is_empty()
&& self.engines.is_empty()
&& self.torpedoes.is_empty()
&& self.artillery.is_empty()
&& self.secondaries.is_empty()
&& self.fire_controls.is_empty()
}
pub fn hull(&self, upgrade_name: &str) -> Option<&HullComponentStats> {
self.hulls.get(upgrade_name)
}
pub fn engine(&self, upgrade_name: &str) -> Option<&EngineComponentStats> {
self.engines.get(upgrade_name)
}
pub fn torpedoes(&self, upgrade_name: &str) -> Option<&[TorpedoLauncherStats]> {
self.torpedoes.get(upgrade_name).map(|v| v.as_slice())
}
pub fn artillery(&self, upgrade_name: &str) -> Option<&ArtilleryComponentStats> {
self.artillery.get(upgrade_name)
}
pub fn secondaries(&self, upgrade_name: &str) -> Option<&SecondaryComponentStats> {
self.secondaries.get(upgrade_name)
}
pub fn fire_control_max_dist_coef(&self, upgrade_name: &str) -> Option<f32> {
self.fire_controls.get(upgrade_name).copied()
}
pub fn stock_selection(&self) -> &super::selection::ShipUpgradeSelection {
&self.stock_selection
}
}
impl ArtilleryGunStats {
pub fn dispersion_curve(&self) -> Option<DispersionCurve> {
Some(DispersionCurve {
min_radius: self.min_radius?,
ideal_radius: self.ideal_radius?,
ideal_distance: self.ideal_distance?,
radius_on_zero: self.radius_on_zero?,
radius_on_delim: self.radius_on_delim?,
radius_on_max: self.radius_on_max?,
delim: self.delim?,
})
}
}
#[cfg(test)]
mod tests {
use super::ArtilleryGunStats;
#[test]
fn dispersion_curve_fields_default_none_and_hold_values() {
let stock = ArtilleryGunStats::default();
assert!(stock.radius_on_zero.is_none());
assert!(stock.delim.is_none());
let g = ArtilleryGunStats { radius_on_max: Some(1.8), delim: Some(0.5), ..Default::default() };
assert_eq!(g.radius_on_max, Some(1.8));
assert_eq!(g.delim, Some(0.5));
}
#[test]
fn dispersion_curve_is_all_or_nothing() {
let partial = ArtilleryGunStats {
min_radius: Some(2.0),
ideal_radius: Some(12.0),
ideal_distance: Some(1000.0),
radius_on_zero: Some(1.0),
radius_on_delim: Some(1.4),
radius_on_max: Some(1.8),
delim: None,
..Default::default()
};
assert!(partial.dispersion_curve().is_none());
let full = ArtilleryGunStats { delim: Some(0.5), ..partial };
let curve = full.dispersion_curve().expect("all fields present");
assert_eq!(curve.radius_on_max, 1.8);
assert_eq!(curve.delim, 0.5);
}
}