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.

149 lines
5.1 KiB

use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use rand::Rng;
use rand::distributions::{WeightedIndex, Distribution};
use entity::character::SectionID;
use entity::item::armor::{ArmorType, Armor};
use maps::room::{Difficulty, Episode};
use maps::area::MapArea;
use crate::ship::drops::{ItemDropType, load_data_file};
use crate::ship::item_stats::{armor_stats, ArmorStats};
#[derive(Debug, Serialize, Deserialize)]
struct ArmorRankRates {
rank0: u32,
rank1: u32,
rank2: u32,
rank3: u32,
rank4: u32,
}
#[derive(Debug, Serialize, Deserialize)]
struct ArmorSlotRanks {
slot0: u32,
slot1: u32,
slot2: u32,
slot3: u32,
slot4: u32,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GenericArmorTable {
rank_rates: ArmorRankRates,
slot_rates: ArmorSlotRanks,
armor_set: u32,
#[serde(skip)]
armor_stats: HashMap<ArmorType, ArmorStats>,
}
impl GenericArmorTable {
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> GenericArmorTable {
let mut gat: GenericArmorTable = load_data_file(episode, difficulty, section_id, "armor_rate.toml");
gat.armor_stats = armor_stats();
gat
}
fn armor_type<R: Rng>(&self, area_map: &MapArea, rng: &mut R) -> ArmorType {
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 armor_level = std::cmp::max(0i32, self.armor_set as i32 - 3i32 + rank + area_map.drop_area_value().unwrap_or(0) as i32);
match armor_level {
0x00 => ArmorType::Frame,
0x01 => ArmorType::Armor,
0x02 => ArmorType::PsyArmor,
0x03 => ArmorType::GigaFrame,
0x04 => ArmorType::SoulFrame,
0x05 => ArmorType::CrossArmor,
0x06 => ArmorType::SolidFrame,
0x07 => ArmorType::BraveArmor,
0x08 => ArmorType::HyperFrame,
0x09 => ArmorType::GrandArmor,
0x0A => ArmorType::ShockFrame,
0x0B => ArmorType::KingsFrame,
0x0C => ArmorType::DragonFrame,
0x0D => ArmorType::AbsorbArmor,
0x0E => ArmorType::ProtectFrame,
0x0F => ArmorType::GeneralArmor,
0x10 => ArmorType::PerfectFrame,
0x11 => ArmorType::ValiantFrame,
0x12 => ArmorType::ImperialArmor,
0x13 => ArmorType::HolinessArmor,
0x14 => ArmorType::GuardianArmor,
0x15 => ArmorType::DivinityArmor,
0x16 => ArmorType::UltimateFrame,
0x17 => ArmorType::CelestialArmor,
_ => panic!()
}
}
pub fn slots<R: Rng>(&self, _area_map: &MapArea, rng: &mut R) -> usize {
let slot_weights = WeightedIndex::new([self.slot_rates.slot0, self.slot_rates.slot1, self.slot_rates.slot2,
self.slot_rates.slot3, self.slot_rates.slot4]).unwrap();
slot_weights.sample(rng)
}
pub fn dfp_modifier<R: Rng>(&self, armor_type: &ArmorType, rng: &mut R) -> u32 {
let stats = self.armor_stats.get(armor_type).unwrap();
rng.gen_range(0, stats.dfp_modifier)
}
pub fn evp_modifier<R: Rng>(&self, armor_type: &ArmorType, rng: &mut R) -> u32 {
let stats = self.armor_stats.get(armor_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 armor_type = self.armor_type(area_map, rng);
let slots = self.slots(area_map, rng);
let dfp_modifier = self.dfp_modifier(&armor_type, rng);
let evp_modifier = self.dfp_modifier(&armor_type, rng);
Some(ItemDropType::Armor(Armor {
armor: armor_type,
dfp: dfp_modifier as u8,
evp: evp_modifier as u8,
slots: slots as u8,
}))
}
}
#[cfg(test)]
mod test {
use super::*;
use rand::{SeedableRng};
#[test]
fn test_armor_generation() {
let mut rng = rand_chacha::ChaCha20Rng::from_seed([23;32]);
let gat = GenericArmorTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly);
assert!(gat.get_drop(&MapArea::Mines1, &mut rng) == Some(ItemDropType::Armor(Armor {
armor: ArmorType::GeneralArmor,
dfp: 0,
evp: 0,
slots: 1,
})));
assert!(gat.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Armor(Armor {
armor: ArmorType::AbsorbArmor,
dfp: 1,
evp: 1,
slots: 1,
})));
assert!(gat.get_drop(&MapArea::Forest2, &mut rng) == Some(ItemDropType::Armor(Armor {
armor: ArmorType::HyperFrame,
dfp: 0,
evp: 0,
slots: 0,
})));
assert!(gat.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Armor(Armor {
armor: ArmorType::ImperialArmor,
dfp: 2,
evp: 1,
slots: 0,
})));
}
}