135 lines
4.9 KiB
Rust
135 lines
4.9 KiB
Rust
use std::collections::{BTreeMap};
|
|
use serde::{Serialize, Deserialize};
|
|
use rand::{Rng};
|
|
use rand::distributions::{WeightedIndex, Distribution};
|
|
|
|
use entity::item::tech::{Technique, TechniqueDisk};
|
|
use maps::room::{Difficulty, Episode};
|
|
use maps::area::MapArea;
|
|
use entity::character::SectionID;
|
|
use crate::ship::drops::{ItemDropType, load_data_file};
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct TechniqueRateStat {
|
|
rate: u32,
|
|
min: i32,
|
|
max: i32,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct TechniqueRatesRaw {
|
|
area1: BTreeMap<String, TechniqueRateStat>,
|
|
area2: BTreeMap<String, TechniqueRateStat>,
|
|
area3: BTreeMap<String, TechniqueRateStat>,
|
|
area4: BTreeMap<String, TechniqueRateStat>,
|
|
area5: BTreeMap<String, TechniqueRateStat>,
|
|
area6: BTreeMap<String, TechniqueRateStat>,
|
|
area7: BTreeMap<String, TechniqueRateStat>,
|
|
area8: BTreeMap<String, TechniqueRateStat>,
|
|
area9: BTreeMap<String, TechniqueRateStat>,
|
|
area10: BTreeMap<String, TechniqueRateStat>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct TechniqueRates {
|
|
area1: BTreeMap<Technique, TechniqueRateStat>,
|
|
area2: BTreeMap<Technique, TechniqueRateStat>,
|
|
area3: BTreeMap<Technique, TechniqueRateStat>,
|
|
area4: BTreeMap<Technique, TechniqueRateStat>,
|
|
area5: BTreeMap<Technique, TechniqueRateStat>,
|
|
area6: BTreeMap<Technique, TechniqueRateStat>,
|
|
area7: BTreeMap<Technique, TechniqueRateStat>,
|
|
area8: BTreeMap<Technique, TechniqueRateStat>,
|
|
area9: BTreeMap<Technique, TechniqueRateStat>,
|
|
area10: BTreeMap<Technique, TechniqueRateStat>,
|
|
}
|
|
|
|
impl TechniqueRates {
|
|
fn new(rates: TechniqueRatesRaw) -> TechniqueRates {
|
|
TechniqueRates {
|
|
area1: rates.area1.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
|
|
area2: rates.area2.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
|
|
area3: rates.area3.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
|
|
area4: rates.area4.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
|
|
area5: rates.area5.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
|
|
area6: rates.area6.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
|
|
area7: rates.area7.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
|
|
area8: rates.area8.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
|
|
area9: rates.area9.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
|
|
area10: rates.area10.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TechniqueRates {
|
|
fn get_by_area<'a>(&'a self, map_area: &MapArea) -> &'a BTreeMap<Technique, TechniqueRateStat> {
|
|
match map_area.drop_area_value().unwrap() {
|
|
0 => &self.area1,
|
|
1 => &self.area2,
|
|
2 => &self.area3,
|
|
3 => &self.area4,
|
|
4 => &self.area5,
|
|
5 => &self.area6,
|
|
6 => &self.area7,
|
|
7 => &self.area8,
|
|
8 => &self.area9,
|
|
_ => &self.area10,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct TechniqueTable {
|
|
rates: TechniqueRates
|
|
}
|
|
|
|
impl TechniqueTable {
|
|
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> TechniqueTable {
|
|
let rates: TechniqueRatesRaw = load_data_file(episode, difficulty, section_id, "tech_rate.toml");
|
|
|
|
TechniqueTable {
|
|
rates: TechniqueRates::new(rates),
|
|
}
|
|
}
|
|
|
|
|
|
pub fn get_drop<R: Rng>(&self, map_area: &MapArea, rng: &mut R) -> Option<ItemDropType> {
|
|
let mut tech_rates = self.rates.get_by_area(map_area).iter();
|
|
let tech_weights = WeightedIndex::new(tech_rates.clone().map(|(_, stat)| stat.rate)).unwrap();
|
|
|
|
let (tech, stat) = tech_rates.nth(tech_weights.sample(rng)).unwrap();
|
|
let level = rng.gen_range(stat.min, stat.max+1) + 1;
|
|
|
|
Some(ItemDropType::TechniqueDisk(TechniqueDisk {
|
|
tech: *tech,
|
|
level: level as u32
|
|
}))
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
use rand::{SeedableRng};
|
|
#[test]
|
|
fn test_tech_drops() {
|
|
let mut rng = rand_chacha::ChaCha20Rng::from_seed([23;32]);
|
|
let tt = TechniqueTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly);
|
|
|
|
let tech_tests = vec![(MapArea::Forest1, Technique::Resta, 14),
|
|
(MapArea::Caves3, Technique::Foie, 25),
|
|
(MapArea::Mines2, Technique::Gibarta, 21),
|
|
(MapArea::DarkFalz, Technique::Razonde, 23)];
|
|
|
|
for (area, tech, level) in tech_tests {
|
|
assert!(tt.get_drop(&area, &mut rng) == Some(ItemDropType::TechniqueDisk(
|
|
TechniqueDisk {
|
|
tech: tech,
|
|
level: level
|
|
})));
|
|
}
|
|
}
|
|
}
|