use std::collections::BTreeMap;
use serde::Serialize;
#[cfg(feature = "typescript")]
use tsify::Tsify;
use esf_data::Info;
use super::Objects;
use super::item::{EffectOperator, Item, Object};
use super::outgoing::outgoing;
use crate::fit::State;
use crate::projection::{ProjectedBuff, Projection};
use crate::validate::Violation;
#[cfg_attr(feature = "typescript", derive(Tsify))]
#[derive(Serialize, Debug)]
pub struct Calculation {
pub ship: ItemResult,
#[serde(skip_serializing_if = "Option::is_none")]
pub mode: Option<ItemResult>,
pub items: Vec<ItemResult>,
pub character: ItemResult,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[cfg_attr(feature = "typescript", tsify(optional))]
pub buffs: Vec<ProjectedBuff>,
#[serde(skip_serializing_if = "Projection::is_empty")]
#[cfg_attr(feature = "typescript", tsify(optional))]
pub outgoing: Projection,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "typescript", tsify(optional))]
pub violations: Option<Vec<Violation>>,
}
#[cfg_attr(feature = "typescript", derive(Tsify))]
#[derive(Serialize, Debug)]
pub struct ItemResult {
pub attributes: BTreeMap<i32, AttributeValue>,
pub state: State,
pub max_state: State,
pub charge: Option<Box<ItemResult>>,
}
#[cfg_attr(feature = "typescript", derive(Tsify))]
#[derive(Serialize, Debug)]
pub struct AttributeValue {
pub base: f64,
pub value: f64,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[cfg_attr(feature = "typescript", tsify(optional))]
pub sources: Vec<Source>,
}
#[cfg_attr(feature = "typescript", derive(Tsify))]
#[derive(Serialize, Debug, Clone)]
pub struct Source {
pub from: SourceRef,
pub effect_id: Option<i32>,
pub source_attribute_id: Option<i32>,
pub operator: EffectOperator,
pub value: f64,
pub quantity: u32,
pub penalty: Option<f64>,
pub applied: bool,
}
#[cfg_attr(feature = "typescript", derive(Tsify))]
#[derive(Serialize, Debug, Clone, Copy, PartialEq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum SourceRef {
Ship,
Mode,
Character,
Item {
index: usize,
},
Charge {
index: usize,
},
Skill {
type_id: i32,
},
Projected {
index: usize,
},
Buff {
id: i32,
},
}
impl SourceRef {
pub(super) fn new(object: Object, type_id: i32) -> SourceRef {
match object {
Object::Ship => SourceRef::Ship,
Object::Mode => SourceRef::Mode,
Object::Char => SourceRef::Character,
Object::Item(index) => SourceRef::Item { index },
Object::Charge(index) => SourceRef::Charge { index },
Object::Skill(_) => SourceRef::Skill { type_id },
Object::Projected(index) => SourceRef::Projected { index },
}
}
}
impl ItemResult {
fn new(item: &Item) -> ItemResult {
ItemResult {
attributes: item
.attributes
.iter()
.map(|(attribute_id, attribute)| {
let value = AttributeValue {
base: attribute.base_value,
value: attribute.value.get().unwrap_or(attribute.base_value),
sources: attribute.sources.borrow().clone(),
};
(*attribute_id, value)
})
.collect(),
state: item.state.into(),
max_state: item.max_state.into(),
charge: item
.charge
.as_deref()
.map(|charge| Box::new(ItemResult::new(charge))),
}
}
}
impl Calculation {
pub(super) fn new(info: &impl Info, objects: &Objects) -> Calculation {
Calculation {
ship: ItemResult::new(&objects.ship),
mode: objects.mode.as_ref().map(ItemResult::new),
items: objects.items.iter().map(ItemResult::new).collect(),
character: ItemResult::new(&objects.char),
buffs: objects.buffs.clone(),
outgoing: outgoing(info, objects),
violations: None,
}
}
}