use std::collections::HashMap; use serde::{Serialize, Deserialize}; use rand::{Rng, SeedableRng}; use rand::distributions::{WeightedIndex, Distribution}; use crate::entity::item::{ItemDetail, Shield as ShieldDetail}; use crate::entity::item::shield::{ShieldType, Shield}; use crate::ship::room::{Difficulty, Episode}; use crate::ship::map::MapVariantType; use crate::entity::character::SectionID; use crate::ship::drops::load_data_file; use crate::ship::item_stats::{shield_stats, ShieldStats}; #[derive(Debug, Serialize, Deserialize)] struct ShieldRankRates { rank0: u32, rank1: u32, rank2: u32, rank3: u32, rank4: u32, } #[derive(Debug, Serialize, Deserialize)] pub struct GenericShieldTable { rank_rates: ShieldRankRates, shield_set: u32, #[serde(skip)] shield_stats: HashMap, } impl GenericShieldTable { pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> GenericShieldTable { let mut gst: GenericShieldTable = load_data_file(episode, difficulty, section_id, "shield_rate.toml"); gst.shield_stats = shield_stats(); gst } fn shield_type(&self, area_map: &MapVariantType, rng: &mut R) -> ShieldType { let rank_weights = WeightedIndex::new(&[self.rank_rates.rank0, self.rank_rates.rank1, self.rank_rates.rank2, self.rank_rates.rank3, self.rank_rates.rank4]).unwrap(); let rank = rank_weights.sample(rng) as i32; let shield_level = std::cmp::max(0i32, self.shield_set as i32 - 3i32 + rank + area_map.area_value().unwrap_or(0) as i32); match shield_level { 0x00 => ShieldType::Barrier, 0x01 => ShieldType::Shield, 0x02 => ShieldType::CoreShield, 0x03 => ShieldType::GigaShield, 0x04 => ShieldType::SoulBarrier, 0x05 => ShieldType::HardShield, 0x06 => ShieldType::BraveBarrier, 0x07 => ShieldType::SolidShield, 0x08 => ShieldType::FlameBarrier, 0x09 => ShieldType::PlasmaBarrier, 0x0A => ShieldType::FreezeBarrier, 0x0B => ShieldType::PsychicBarrier, 0x0C => ShieldType::GeneralShield, 0x0D => ShieldType::ProtectBarrier, 0x0E => ShieldType::GloriousShield, 0x0F => ShieldType::ImperialBarrier, 0x10 => ShieldType::GuardianShield, 0x11 => ShieldType::DivinityBarrier, 0x12 => ShieldType::UltimateShield, 0x13 => ShieldType::SpiritualShield, 0x14 => ShieldType::CelestialShield, _ => panic!(), } } // TODO: this needs the pmt file fn dfp_modifier(&self, shield_type: &ShieldType, rng: &mut R) -> u32 { let stats = self.shield_stats.get(shield_type).unwrap(); rng.gen_range(0, stats.dfp_modifier) } // TODO: this needs the pmt file fn evp_modifier(&self, shield_type: &ShieldType, rng: &mut R) -> u32 { let stats = self.shield_stats.get(shield_type).unwrap(); rng.gen_range(0, stats.evp_modifier) } pub fn get_drop(&self, area_map: &MapVariantType, rng: &mut R) -> Option { let shield_type = self.shield_type(area_map, rng); let dfp_modifier = self.dfp_modifier(&shield_type, rng); let evp_modifier = self.dfp_modifier(&shield_type, rng); Some(ItemDetail::Shield(ShieldDetail { equipped: false, shield: Shield { shield: shield_type, dfp: dfp_modifier as u8, evp: evp_modifier as u8, } })) } } #[cfg(test)] mod test { use super::*; #[test] fn test_shield_generation() { let mut rng = rand_chacha::ChaCha20Rng::from_seed([23;32]); let gst = GenericShieldTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly); //println!("{:?}", gst.get_drop(&MapVariantType::Forest1, &mut rng)); //println!("{:?}", gst.get_drop(&MapVariantType::Caves3, &mut rng)); //println!("{:?}", gst.get_drop(&MapVariantType::Mines2, &mut rng)); //println!("{:?}", gst.get_drop(&MapVariantType::DarkFalz, &mut rng)); assert!(gst.get_drop(&MapVariantType::Forest1, &mut rng) == Some(ItemDetail::Shield(ShieldDetail { equipped: false, shield: Shield { shield: ShieldType::FreezeBarrier, dfp: 4, evp: 1, } }))); assert!(gst.get_drop(&MapVariantType::Caves3, &mut rng) == Some(ItemDetail::Shield(ShieldDetail { equipped: false, shield: Shield { shield: ShieldType::PsychicBarrier, dfp: 3, evp: 2, } }))); assert!(gst.get_drop(&MapVariantType::Mines2, &mut rng) == Some(ItemDetail::Shield(ShieldDetail { equipped: false, shield: Shield { shield: ShieldType::ImperialBarrier, dfp: 0, evp: 4, } }))); assert!(gst.get_drop(&MapVariantType::DarkFalz, &mut rng) == Some(ItemDetail::Shield(ShieldDetail { equipped: false, shield: Shield { shield: ShieldType::DivinityBarrier, dfp: 1, evp: 0, } }))); } }