use serde::{Deserialize, Serialize};
use crate::buff_types::BuffableStat;
use crate::types::Element;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ElementalResonance {
FerventFlames,
SoothingWater,
HighVoltage,
ShatteringIce,
ImpetuousWinds,
EnduringRock,
SprawlingGreenery,
ProtectiveCanopy,
}
pub fn determine_resonances(elements: &[Element]) -> Vec<ElementalResonance> {
if elements.len() != 4 {
return vec![];
}
use std::collections::HashMap;
let mut counts: HashMap<Element, usize> = HashMap::new();
for &e in elements {
*counts.entry(e).or_insert(0) += 1;
}
if counts.len() == 4 {
return vec![ElementalResonance::ProtectiveCanopy];
}
let mut resonances = vec![];
for (&element, &count) in &counts {
if count >= 2 {
let r = match element {
Element::Pyro => ElementalResonance::FerventFlames,
Element::Hydro => ElementalResonance::SoothingWater,
Element::Electro => ElementalResonance::HighVoltage,
Element::Cryo => ElementalResonance::ShatteringIce,
Element::Anemo => ElementalResonance::ImpetuousWinds,
Element::Geo => ElementalResonance::EnduringRock,
Element::Dendro => ElementalResonance::SprawlingGreenery,
};
resonances.push(r);
}
}
resonances.sort_by_key(|r| *r as u8);
resonances
}
pub fn resonance_buffs(resonance: ElementalResonance) -> Vec<(BuffableStat, f64)> {
match resonance {
ElementalResonance::FerventFlames => {
vec![(BuffableStat::AtkPercent, 0.25)]
}
ElementalResonance::SoothingWater => {
vec![(BuffableStat::HpPercent, 0.25)]
}
ElementalResonance::SprawlingGreenery => {
vec![(BuffableStat::ElementalMastery, 50.0)]
}
_ => vec![],
}
}
pub fn resonance_conditional_buffs(resonance: ElementalResonance) -> Vec<(BuffableStat, f64)> {
match resonance {
ElementalResonance::ShatteringIce => {
vec![(BuffableStat::CritRate, 0.15)]
}
ElementalResonance::EnduringRock => {
vec![(BuffableStat::DmgBonus, 0.15)]
}
ElementalResonance::SprawlingGreenery => {
vec![(BuffableStat::ElementalMastery, 30.0)]
}
_ => vec![],
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fewer_than_4_returns_empty() {
assert!(determine_resonances(&[Element::Pyro, Element::Pyro]).is_empty());
assert!(determine_resonances(&[Element::Pyro, Element::Pyro, Element::Hydro]).is_empty());
assert!(determine_resonances(&[]).is_empty());
}
#[test]
fn test_pyro_resonance() {
let elements = [Element::Pyro, Element::Pyro, Element::Hydro, Element::Cryo];
let res = determine_resonances(&elements);
assert!(res.contains(&ElementalResonance::FerventFlames));
assert_eq!(res.len(), 1);
}
#[test]
fn test_double_resonance() {
let elements = [Element::Pyro, Element::Pyro, Element::Hydro, Element::Hydro];
let res = determine_resonances(&elements);
assert!(res.contains(&ElementalResonance::FerventFlames));
assert!(res.contains(&ElementalResonance::SoothingWater));
assert_eq!(res.len(), 2);
}
#[test]
fn test_four_unique_elements() {
let elements = [
Element::Pyro,
Element::Hydro,
Element::Electro,
Element::Cryo,
];
let res = determine_resonances(&elements);
assert_eq!(res, vec![ElementalResonance::ProtectiveCanopy]);
}
#[test]
fn test_triple_same_element() {
let elements = [Element::Pyro, Element::Pyro, Element::Pyro, Element::Hydro];
let res = determine_resonances(&elements);
assert!(res.contains(&ElementalResonance::FerventFlames));
assert_eq!(res.len(), 1);
}
#[test]
fn test_fervent_flames_buffs() {
let buffs = resonance_buffs(ElementalResonance::FerventFlames);
assert_eq!(buffs.len(), 1);
assert_eq!(buffs[0], (BuffableStat::AtkPercent, 0.25));
}
#[test]
fn test_soothing_water_buffs() {
let buffs = resonance_buffs(ElementalResonance::SoothingWater);
assert_eq!(buffs.len(), 1);
assert_eq!(buffs[0], (BuffableStat::HpPercent, 0.25));
}
#[test]
fn test_sprawling_greenery_buffs() {
let buffs = resonance_buffs(ElementalResonance::SprawlingGreenery);
assert_eq!(buffs.len(), 1);
assert_eq!(buffs[0], (BuffableStat::ElementalMastery, 50.0));
}
#[test]
fn test_conditional_resonance_no_buffs() {
assert!(resonance_buffs(ElementalResonance::ShatteringIce).is_empty());
assert!(resonance_buffs(ElementalResonance::EnduringRock).is_empty());
assert!(resonance_buffs(ElementalResonance::HighVoltage).is_empty());
assert!(resonance_buffs(ElementalResonance::ImpetuousWinds).is_empty());
assert!(resonance_buffs(ElementalResonance::ProtectiveCanopy).is_empty());
}
#[test]
fn test_shattering_ice_conditional_buffs() {
let buffs = resonance_conditional_buffs(ElementalResonance::ShatteringIce);
assert_eq!(buffs.len(), 1);
assert_eq!(buffs[0], (BuffableStat::CritRate, 0.15));
}
#[test]
fn test_enduring_rock_conditional_buffs() {
let buffs = resonance_conditional_buffs(ElementalResonance::EnduringRock);
assert_eq!(buffs.len(), 1);
assert_eq!(buffs[0], (BuffableStat::DmgBonus, 0.15));
}
#[test]
fn test_sprawling_greenery_conditional_buffs() {
let buffs = resonance_conditional_buffs(ElementalResonance::SprawlingGreenery);
assert_eq!(buffs.len(), 1);
assert_eq!(buffs[0], (BuffableStat::ElementalMastery, 30.0));
}
#[test]
fn test_unconditional_resonances_have_no_conditional_buffs() {
assert!(resonance_conditional_buffs(ElementalResonance::FerventFlames).is_empty());
assert!(resonance_conditional_buffs(ElementalResonance::SoothingWater).is_empty());
assert!(resonance_conditional_buffs(ElementalResonance::HighVoltage).is_empty());
assert!(resonance_conditional_buffs(ElementalResonance::ImpetuousWinds).is_empty());
assert!(resonance_conditional_buffs(ElementalResonance::ProtectiveCanopy).is_empty());
}
}