You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
4.4 KiB
125 lines
4.4 KiB
use std::collections::HashMap;
|
|
use serde::{Serialize, Deserialize};
|
|
use rand::Rng;
|
|
use rand::distributions::{WeightedIndex, Distribution};
|
|
|
|
use entity::item::shield::{ShieldType, Shield};
|
|
use entity::character::SectionID;
|
|
use maps::room::{Difficulty, Episode};
|
|
use maps::area::MapArea;
|
|
use crate::{ItemDropType, load_data_file};
|
|
use stats::items::{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<ShieldType, ShieldStats>,
|
|
}
|
|
|
|
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<R: Rng>(&self, area_map: &MapArea, 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.drop_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!(),
|
|
}
|
|
}
|
|
|
|
pub fn dfp_modifier<R: Rng>(&self, shield_type: &ShieldType, rng: &mut R) -> u32 {
|
|
let stats = self.shield_stats.get(shield_type).unwrap();
|
|
rng.gen_range(0, stats.dfp_modifier)
|
|
}
|
|
|
|
pub fn evp_modifier<R: Rng>(&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<R: Rng>(&self, area_map: &MapArea, rng: &mut R) -> Option<ItemDropType> {
|
|
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(ItemDropType::Shield(Shield {
|
|
shield: shield_type,
|
|
dfp: dfp_modifier as u8,
|
|
evp: evp_modifier as u8,
|
|
}))
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
use rand::{SeedableRng};
|
|
#[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);
|
|
|
|
assert!(gst.get_drop(&MapArea::Forest1, &mut rng) == Some(ItemDropType::Shield(Shield {
|
|
shield: ShieldType::FreezeBarrier,
|
|
dfp: 4,
|
|
evp: 1,
|
|
})));
|
|
assert!(gst.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Shield(Shield {
|
|
shield: ShieldType::PsychicBarrier,
|
|
dfp: 3,
|
|
evp: 2,
|
|
})));
|
|
assert!(gst.get_drop(&MapArea::Mines2, &mut rng) == Some(ItemDropType::Shield(Shield {
|
|
shield: ShieldType::ImperialBarrier,
|
|
dfp: 0,
|
|
evp: 4,
|
|
})));
|
|
assert!(gst.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Shield(Shield {
|
|
shield: ShieldType::DivinityBarrier,
|
|
dfp: 1,
|
|
evp: 0,
|
|
})));
|
|
}
|
|
}
|