172 lines
4.5 KiB
Rust
172 lines
4.5 KiB
Rust
//! Elite: Dangerous Journal Loadout even parser
|
|
use crate::common::get_fsd_info;
|
|
use crate::ship::Ship;
|
|
|
|
use eyre::Result;
|
|
use regex::Regex;
|
|
use serde::Deserialize;
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
|
pub struct Event {
|
|
#[serde(flatten)]
|
|
pub event: EventData,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
|
#[serde(tag = "event")]
|
|
pub enum EventData {
|
|
Loadout(Loadout),
|
|
#[serde(other)]
|
|
Unknown,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
|
#[serde(rename_all = "PascalCase")]
|
|
pub struct Modifier {
|
|
label: String,
|
|
value: f32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
|
#[serde(rename_all = "PascalCase")]
|
|
pub struct Engineering {
|
|
modifiers: Vec<Modifier>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
|
#[serde(rename_all = "PascalCase")]
|
|
pub struct Module {
|
|
engineering: Option<Engineering>,
|
|
item: String,
|
|
slot: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
|
#[serde(rename_all = "PascalCase")]
|
|
pub struct FuelCapacity {
|
|
main: f32,
|
|
reserve: f32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
|
#[serde(rename_all = "PascalCase")]
|
|
pub struct Loadout {
|
|
#[serde(rename = "timestamp")]
|
|
timestamp: String,
|
|
ship: String,
|
|
ship_name: String,
|
|
ship_ident: String,
|
|
fuel_capacity: FuelCapacity,
|
|
unladen_mass: f32,
|
|
modules: Vec<Module>,
|
|
}
|
|
|
|
impl Engineering {
|
|
fn to_hashmap(&self) -> HashMap<String, f32> {
|
|
let mut h = HashMap::new();
|
|
for v in &self.modifiers {
|
|
h.insert(v.label.clone(), v.value);
|
|
}
|
|
return h;
|
|
}
|
|
}
|
|
|
|
impl Loadout {
|
|
fn get_booster(&self) -> Option<usize> {
|
|
self.modules.iter().cloned().find_map(|m| {
|
|
let Module { item, .. } = m;
|
|
if item.starts_with("int_guardianfsdbooster") {
|
|
return item
|
|
.chars()
|
|
.last()
|
|
.unwrap()
|
|
.to_digit(10)
|
|
.map(|v| v as usize);
|
|
}
|
|
return None;
|
|
})
|
|
}
|
|
|
|
fn get_fsd(&self) -> Option<(String, Option<Engineering>)> {
|
|
self.modules.iter().cloned().find_map(|m| {
|
|
let Module {
|
|
slot,
|
|
engineering,
|
|
item,
|
|
} = m;
|
|
if slot == "FrameShiftDrive" {
|
|
return Some((item, engineering));
|
|
}
|
|
return None;
|
|
})
|
|
}
|
|
|
|
pub fn try_into_ship(self) -> Result<(String, Ship), String> {
|
|
let fsd = self.get_fsd().ok_or("No FSD found!")?;
|
|
let booster = self.get_booster().unwrap_or(0);
|
|
let fsd_type = Regex::new(r"^int_hyperdrive_size(\d+)_class(\d+)$")
|
|
.unwrap()
|
|
.captures(&fsd.0);
|
|
let fsd_type: (usize, usize) = fsd_type
|
|
.and_then(|m| {
|
|
let s = m.get(1)?.as_str().to_owned().parse().ok()?;
|
|
let c = m.get(2)?.as_str().to_owned().parse().ok()?;
|
|
return Some((c, s));
|
|
})
|
|
.ok_or(format!("Invalid FSD found: {}", &fsd.0))?;
|
|
let eng = fsd
|
|
.1
|
|
.map(|eng| eng.to_hashmap())
|
|
.unwrap_or_else(HashMap::new);
|
|
let mut fsd_info = get_fsd_info(fsd_type.0, fsd_type.1)?;
|
|
let fsd_type = (
|
|
"_EDCBA"
|
|
.chars()
|
|
.nth(fsd_type.0)
|
|
.ok_or(format!("Invalid FSD found: {}", &fsd.0))?,
|
|
fsd_type.1 as u8,
|
|
);
|
|
fsd_info.extend(eng);
|
|
let max_fuel = fsd_info
|
|
.get("MaxFuel")
|
|
.ok_or(format!("Unknwon MaxFuelPerJump for FSD: {}", &fsd.0))?;
|
|
let opt_mass = fsd_info
|
|
.get("FSDOptimalMass")
|
|
.ok_or(format!("Unknwon FSDOptimalMass for FSD: {}", &fsd.0))?;
|
|
let key = format!(
|
|
"[{}] {} ({})",
|
|
if !self.ship_name.is_empty() {
|
|
self.ship_name
|
|
} else {
|
|
"<NO NAME>".to_owned()
|
|
},
|
|
self.ship_ident,
|
|
self.ship
|
|
);
|
|
return Ok((
|
|
key,
|
|
Ship::new(
|
|
self.unladen_mass,
|
|
self.fuel_capacity.main,
|
|
self.fuel_capacity.main,
|
|
fsd_type,
|
|
*max_fuel,
|
|
*opt_mass,
|
|
booster,
|
|
)?,
|
|
));
|
|
}
|
|
}
|
|
|
|
impl Event {
|
|
pub fn get_loadout(self) -> Option<Loadout> {
|
|
if let Event {
|
|
event: EventData::Loadout(loadout),
|
|
} = self
|
|
{
|
|
return Some(loadout);
|
|
}
|
|
None
|
|
}
|
|
}
|