elseware/src/ship/drops/tech_table.rs

136 lines
5.0 KiB
Rust
Raw Normal View History

2020-03-16 18:47:30 -07:00
use std::collections::{HashMap, BTreeMap};
use std::io::Read;
use serde::{Serialize, Deserialize};
use rand::{Rng, SeedableRng};
use rand::distributions::{WeightedIndex, Distribution};
2020-03-18 18:21:34 -07:00
use crate::entity::item::ItemDetail;
use crate::entity::item::tech::{Technique, TechniqueDisk};
2020-03-16 18:47:30 -07:00
use crate::ship::room::{Difficulty, Episode};
use crate::ship::map::MapVariantType;
use crate::entity::character::SectionID;
use crate::ship::drops::load_data_file;
2020-03-18 18:21:34 -07:00
#[derive(Debug, Serialize, Deserialize)]
struct TechniqueRateStat {
rate: u32,
min: i32,
max: i32,
}
2020-03-16 18:47:30 -07:00
2020-03-18 18:21:34 -07:00
#[derive(Debug, Serialize, Deserialize)]
2020-03-22 00:13:31 -07:00
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>,
2020-03-18 18:21:34 -07:00
}
2020-03-16 18:47:30 -07:00
2020-03-18 18:21:34 -07:00
#[derive(Debug, Serialize, Deserialize)]
struct TechniqueRates {
2020-03-22 00:13:31 -07:00
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>,
2020-03-18 18:21:34 -07:00
}
2020-03-16 18:47:30 -07:00
2020-03-22 00:13:31 -07:00
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: &MapVariantType) -> &'a BTreeMap<Technique, TechniqueRateStat> {
match map_area.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,
}
}
}
2020-03-16 18:47:30 -07:00
pub struct TechniqueTable {
2020-03-18 18:21:34 -07:00
rates: TechniqueRates
2020-03-16 18:47:30 -07:00
}
impl TechniqueTable {
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> TechniqueTable {
2020-03-22 00:13:31 -07:00
let rates: TechniqueRatesRaw = load_data_file(episode, difficulty, section_id, "tech_rate.toml");
2020-03-16 18:47:30 -07:00
TechniqueTable {
2020-03-22 00:13:31 -07:00
rates: TechniqueRates::new(rates),
2020-03-16 18:47:30 -07:00
}
}
2020-03-22 00:13:31 -07:00
2020-03-16 18:47:30 -07:00
pub fn get_drop<R: Rng>(&self, map_area: &MapVariantType, rng: &mut R) -> Option<ItemDetail> {
2020-03-22 00:13:31 -07:00
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);
Some(ItemDetail::TechniqueDisk(TechniqueDisk {
tech: *tech,
level: level as u32
}))
}
}
#[cfg(test)]
mod test {
use super::*;
#[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![(MapVariantType::Forest1, Technique::Resta, 13),
(MapVariantType::Caves3, Technique::Foie, 24),
(MapVariantType::Mines2, Technique::Gibarta, 20),
(MapVariantType::DarkFalz, Technique::Razonde, 22)];
for (area, tech, level) in tech_tests {
assert!(tt.get_drop(&area, &mut rng) == Some(ItemDetail::TechniqueDisk(
TechniqueDisk {
tech: tech,
level: level
})));
}
2020-03-16 18:47:30 -07:00
}
}